誤區:值得注意的是,需要搞清楚 對符號引用的解析 和類的初始化的區別。
《Java類的裝載(Loading)、鏈接(Linking)和初始化(Initialization) 》一文中提到了, 鏈接的最後一步是resolution , 即對符號引用的解析,但這不是必須的,可以等到相應的符號引用第一次使用時再解析。而類的初始化是在鏈接之後的(注意了,根據不同JVM有不同的實現方式,在類初始化的時候,可能已經完成了所有的符號引用的解析,也可能沒有),本文所寫的就是 類的初始化的時機問題。
Java類的動態加載機制規定,在類被主動使用(active use)之前,必須已經完成類的初始化。
既然有主動調用,那麼就有被動調用了。這兩者有哪些區別呢?
主動使用的情況:
1. 創造該類的一個新的實例
2.調用這個類中的靜態方法
3.獲取類或者接口中的非常量的靜態變量
4.利用反射調用方法。
5.初始該類的某子類。
6.被制定為JVM開始運行時必須初始化的類。
注意:
首先,3中為何是“非常量的靜態變量”。如果是常量,即聲明為final的話,並不會出現對類的構造,雖然調用時有類名出現,但實際調用會直接使用常量,繞過了類的限制(詳情見相關constant pool 和 runtime constant pool的知識)。
只有當一個非常量的靜態變量被顯示的在類或接口中聲明時,它的調用才屬於主動調用。對於父類中某非常兩靜態變量的調用屬於被動使用(positive use),
如下代碼
運行的結果是:
雖然有出現Son,但Son.i訪問的是父類的非常量靜態變量。於是沒有對Son類進行初始化,而只是初始化了明確的聲明靜態變量的Parent類。
由此可見,一般的,我們在某個類中定義了其他類的成員變量引用,只要該變量沒有 new 出一個新的 對象,則JVM也不會初始化這個類,類此時只是被加載了而已,而沒有鏈接 和初始化。