類初始化是類加載過(guò)程的最后一個(gè)階段,到初始化階段,才真正開(kāi)始執(zhí)行類中的 Java 程序代碼。虛擬機(jī)規(guī)范嚴(yán)格規(guī)定了有且只有四種情況必須立即對(duì)類進(jìn)行初始化:
虛擬機(jī)規(guī)定只有這四種情況才會(huì)觸發(fā)類的初始化,稱為對(duì)一個(gè)類進(jìn)行主動(dòng)引用,除此之外所有引用類的方式都不會(huì)觸發(fā)其初始化,稱為被動(dòng)引用。下面舉一些例子來(lái)說(shuō)明被動(dòng)引用。
通過(guò)子類引用父類中的靜態(tài)字段,這時(shí)對(duì)子類的引用為被動(dòng)引用,因此不會(huì)初始化子類,只會(huì)初始化父類:
class Father{
public static int m = 33;
static{
System.out.println("父類被初始化");
}
}
class Child extends Father{
static{
System.out.println("子類被初始化");
}
}
public class StaticTest{
public static void main(String[] args){
System.out.println(Child.m);
}
}
執(zhí)行后輸出的結(jié)果如下:
父類被初始化
33
對(duì)于靜態(tài)字段,只有直接定義這個(gè)字段的類才會(huì)被初始化,因此,通過(guò)其子類來(lái)引用父類中定義的靜態(tài)字段,只會(huì)觸發(fā)父類的初始化而不會(huì)觸發(fā)子類的初始化。
常量在編譯階段會(huì)存入調(diào)用它的類的常量池中,本質(zhì)上沒(méi)有直接引用到定義該常量的類,因此不會(huì)觸發(fā)定義常量的類的初始化:
class Const{
public static final String NAME = "我是常量";
static{
System.out.println("初始化Const類");
}
}
public class FinalTest{
public static void main(String[] args){
System.out.println(Const.NAME);
}
}
執(zhí)行后輸出的結(jié)果如下:
我是常量
雖然程序中引用了 const 類的常量 NAME,但是在編譯階段將此常量的值“我是常量”存儲(chǔ)到了調(diào)用它的類 FinalTest 的常量池中,對(duì)常量 Const.NAME 的引用實(shí)際上轉(zhuǎn)化為了 FinalTest 類對(duì)自身常量池的引用。也就是說(shuō),實(shí)際上 FinalTest 的 Class 文件之中并沒(méi)有 Const 類的符號(hào)引用入口,這兩個(gè)類在編譯成 Class 文件后就不存在任何聯(lián)系了。
通過(guò)數(shù)組定義來(lái)引用類,不會(huì)觸發(fā)類的初始化:
class Const{
static{
System.out.println("初始化Const類");
}
}
public class ArrayTest{
public static void main(String[] args){
Const[] con = new Const[5];
}
}
執(zhí)行后不輸出任何信息,說(shuō)明 Const 類并沒(méi)有被初始化。
但這段代碼里觸發(fā)了另一個(gè)名為“LLConst”的類的初始化,它是一個(gè)由虛擬機(jī)自動(dòng)生成的、直接繼承于java.lang.Object 的子類,創(chuàng)建動(dòng)作由字節(jié)碼指令 newarray 觸發(fā),很明顯,這是一個(gè)對(duì)數(shù)組引用類型的初初始化,而該數(shù)組中的元素僅僅包含一個(gè)對(duì) Const 類的引用,并沒(méi)有對(duì)其進(jìn)行初始化。如果我們加入對(duì) con 數(shù)組中各個(gè) Const 類元素的實(shí)例化代碼,便會(huì)觸發(fā) Const 類的初始化,如下:
class Const{
static{
System.out.println("初始化Const類");
}
}
public class ArrayTest{
public static void main(String[] args){
Const[] con = new Const[5];
for(Const a:con)
a = new Const();
}
}
這樣便會(huì)得到如下輸出結(jié)果:
初始化Const類
根據(jù)四條規(guī)則的第一條,這里的 new 觸發(fā)了 Const 類。
最后看一下接口的初始化過(guò)程與類初始化過(guò)程的不同。
接口也有初始化過(guò)程,上面的代碼中我們都是用靜態(tài)語(yǔ)句塊來(lái)輸出初始化信息的,而在接口中不能使用“static{}”語(yǔ)句塊,但編譯器仍然會(huì)為接口生成類構(gòu)造器,用于初始化接口中定義的成員變量(實(shí)際上是 static final 修飾的全局常量)。
二者在初始化時(shí)最主要的區(qū)別是:當(dāng)一個(gè)類在初始化時(shí),要求其父類全部已經(jīng)初始化過(guò)了,但是一個(gè)接口在初始化時(shí),并不要求其父接口全部都完成了初始化,只有在真正使用到父接口的時(shí)候(如引用接口中定義的常量),才會(huì)初始化該父接口。這點(diǎn)也與類初始化的情況很不同,回過(guò)頭來(lái)看第 2 個(gè)例子就知道,調(diào)用類中的 static final 常量時(shí)并不會(huì) 觸發(fā)該類的初始化,但是調(diào)用接口中的 static final 常量時(shí)便會(huì)觸發(fā)該接口的初始化。
更多建議: