在java中類的加載、初始化都是在程序運行期完成的,雖然會稍微增加開銷,但是卻很大的增加了靈活性,我們可用在運行期間動態的去網絡或其他地方加載一個二進制流來作為程序代碼的一部分。接下來我們簡單介紹下java類加載過程。
從上圖中我們可以看到類的生命周期主要有7個過程,分別為加載、驗證、準備、解析、初始化、使用、卸載。
- 加載:
這個過程很簡單,就是通過ClassLoader去加載Class文件到內存中,當然Class文件的來源并沒有任何限制,既可以是我們的程序通過javac編譯器生成的,也可以來源于網絡等其他途徑。 - 驗證:
這個過程主要就是對Class文件中的字節流進行檢查,看看是否符合規范,是否存在安全問題等等。 - 準備:
這個過程主要是為類變量(static修飾的變量)分配內存并設置初始值,此時的初始值指的是通常情況下的零值,例如private static int a = 3,類變量a在這個階段會被賦值為0。唯一例外的類變量是使用final修飾的類變量,使用final修飾的類變量會在這個階段就賦給程序中設定的值。 - 解析:
這個階段主要將Class文件中對常量池的引用轉為直接內存引用,舉個簡單的例子:我們常會在代碼中使用import xxx.xxx.xxx.class,這里面的xxx.xxx.xxx.class就是符號引用,解析階段就是把這種符號引用轉換為對xxx.class具體內存地址的引用。 - 初始化:
這個階段主要負責初始化類對象(Class對象),會按照在文件中出現的順序為所有類變量賦值,同時執行靜態代碼塊。千萬注意這里初始化的是類對象而不是實例對象! - 使用、卸載:
這兩個階段就沒啥可介紹的了,就是我們程序使用對象的過程,和對象生命周期結束后進行類型卸載的過程。
最后總結下對象靜態屬性/實例屬性初始化的順序:
(1)父類和子類的final static屬性初始化
(2)父類的static屬性初始化、父類的static代碼塊 (兩者按在文件中的聲明順序先后初始化)
(3)子類的static屬性初始化、子類的static代碼塊 (兩者按在文件中的聲明順序先后初始化)
(4)父類的非靜態屬性、父類的非靜態代碼塊 (兩者按在文件中的聲明順序先后初始化)
(5)父類的構造函數執行
(6)子類的非靜態屬性、 子類的非靜態代碼塊 (兩者按在文件中的聲明順序先后初始化)
(7)子類的構造函數執行