原文:https://blog.csdn.net/qq_41063182/article/details/81149798
類加載:第一次調用這個類的時候jvm虛擬機會通過類加載器在一個叫做方法區的邏輯內存中將所要用到的類的信息存放在裏邊,其中方法區有一個靜態區,存放的是類中的靜態(類變量)。
對象構造:在堆中開闢一個內存空間將實例化的對象存放在裏邊,在生命週期中要遠遠小於類。
靜態代碼塊只有在類加載時僅且執行一次
動態代碼塊是在每次構造對象時執行一次,然後纔是構造方法,也就有了一個順序。
靜態代碼塊>動態代碼塊>構造方法,而靜態變量與靜態代碼塊的優先級是一個級別的,這樣看來好像問題解決了,但其實問題並不是這麼簡單。
看一個阿里面試題
public class Test {
public static int k = 0;
public static Test t1 = new Test("t1");
public static Test t2 = new Test("t2");
public static int i = print("i");
public static int n = 99;
private int a = 0;
public int j = print("j");
{
print("構造塊");
}
static {
print("靜態塊");
}
public Test(String str) {
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);
++i;
++n;
}
public static int print(String str) {
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);
++n;
return ++i;
}
public static void main(String args[]) {
Test t = new Test("init");
}
}
結果:
1:j i=0 n=0
2:構造塊 i=1 n=1
3:t1 i=2 n=2
4:j i=3 n=3
5:構造塊 i=4 n=4
6:t2 i=5 n=5
7:i i=6 n=6
8:靜態塊 i=7 n=99
9:j i=8 n=100
10:構造塊 i=9 n=101
11:init i=10 n=102
順序不對:先執行的非靜態的賦值變量,然後動態代碼塊,然後構造方法,靜態代碼塊一次都沒有執行,一直等到第八次才執行出來。那麼問題就來了,java中不是說靜態代碼塊是在類加載時便執行一次,可是這裏爲什麼不執行。
在看一段代碼
package com.basics.day10;
public class TestStatic {
static {
System.out.println("這時TestStatic的靜態代碼塊!");
}
{
System.out.println("這是動態代碼塊!");
}
public TestStatic() {
System.out.println("這是構造代碼塊");
}
public static void main(String[] args) {
TestStatic te = new TestStatic();
TestStatic t2 = new TestStatic();
}
}
// 請注意看這個類中的靜態代碼塊
class Annoy {
static {
System.out.println("Annoy的靜態代碼塊");
}
}
結果:
這時TestStatic的靜態代碼塊!
這是動態代碼塊!
這是構造代碼塊
這是動態代碼塊!
這是構造代碼塊
主類中並沒有引用Annoy類,也就是這個類並沒有被加載進方法區,既然它沒有被加載,那麼其中的代碼塊自然就不會執行了。
類的加載只有在引用它的時候纔會加載,而不是直接加載進方法區的,這也是類加載與構造對象的區別。構造對象並不是類加載,它們是兩個完全不一樣的概念,類加載要在對象構造之前。
折回到阿里巴巴的那道題中,發現他在第二行的時候進行了對象的構造,而在這個構造中執行的都是非靜態的代碼塊。我是這樣理解的,在這個類的main函數被jvm識別的時候,這個類已經加載到方法區中去了,這時候靜態都會被執行一次,而且是按照聲明的順序執行的,正常下來,先k,t1,t2...跳過j,到靜態代碼塊,但這裏t1有了一個變故,那就是他構造了一個對象,這時類已經加載完成,相當於靜態已經默認被執行了一次,只是順序上還沒有輪到,根據靜態的特性,他只執行一次,所以t1這裏的分支,調用的就是動態代碼塊以及構造方法,不會去管那些靜態,這也就導致出現了上邊的那種情況。