前序文章:深入理解Java類加載
<clinit>() 與 <init>() 區(qū)別
<clinit>()
Java 類加載的初始化過程中,編譯器按語句在源文件中出現(xiàn)的順序,依次自動(dòng)收集類中的所有類變量的賦值動(dòng)作和靜態(tài)代碼塊中的語句合并產(chǎn)生 <clinit>() 方法。 如果類中沒有靜態(tài)語句和靜態(tài)代碼塊,那可以不生成<clinit>()` 方法。
并且 <clinit>() 不需要顯式調(diào)用父類(接口除外,接口不需要調(diào)用父接口的初始化方法,只有使用到父接口中的靜態(tài)變量時(shí)才需要調(diào)用)的初始化方法 <clinit>(),虛擬機(jī)會(huì)保證在子類的 <clinit>() 方法執(zhí)行之前,父類的 <clinit>() 方法已經(jīng)執(zhí)行完畢。
<init>()
對(duì)象構(gòu)造時(shí)用以初始化對(duì)象的,構(gòu)造器以及非靜態(tài)初始化塊中的代碼。
<clinit>() 與 <init>() 執(zhí)行順序
直接看代碼
public class Test {
private static Test instance;
static {
System.out.println("static開始");
// 下面這句編譯器報(bào)錯(cuò),非法向前引用
// System.out.println("x=" + x);
instance = new Test();
System.out.println("static結(jié)束");
}
public Test() {
System.out.println("構(gòu)造器開始");
System.out.println("x=" + x + ";y=" + y);
// 構(gòu)造器可以訪問聲明于他們后面的靜態(tài)變量
// 因?yàn)殪o態(tài)變量在類加載的準(zhǔn)備階段就已經(jīng)分配內(nèi)存并初始化0值了
// 此時(shí) x=0,y=0
x++;
y++;
System.out.println("x=" + x + ";y=" + y);
System.out.println("構(gòu)造器結(jié)束");
}
public static int x = 6;
public static int y;
public static Test getInstance() {
return instance;
}
public static void main(String[] args) {
Test obj = Test.getInstance();
System.out.println("x=" + obj.x);
System.out.println("y=" + obj.y);
}
}
輸出信息如下:
static開始
構(gòu)造器開始
x=0;y=0
x=1;y=1
構(gòu)造器結(jié)束
static結(jié)束
x=6
y=1
虛擬機(jī)首先執(zhí)行的是類加載初始化過程中的 <clinit>() 方法,也就是靜態(tài)變量賦值以及靜態(tài)代碼塊中的代碼,如果 <clinit>() 方法中觸發(fā)了對(duì)象的初始化,也就是 <init>() 方法,那么會(huì)進(jìn)入執(zhí)行 <init>() 方法,執(zhí)行 <init>() 方法完成之后,再回來繼續(xù)執(zhí)行 <clinit>() 方法。
上面代碼中,先執(zhí)行 static 代碼塊,此時(shí)調(diào)用了構(gòu)造器,構(gòu)造器中對(duì)類變量 x 和 y 進(jìn)行加 1 ,之后繼續(xù)完 static 代碼塊,接著執(zhí)行下面的 public static int x = 6; 來重新給類變量 x 賦值為 6,因此,最后輸出的是 x=6, y=1。
如果希望輸出的是 x=7,y=1,很簡單,將語句 public static int x = 6; 移至 static 代碼塊之前就可以了。