在寫一個通用的報警模塊時,遇到一個有意思的問題,在調用靜態方法時,發現靜態方法內部對靜態變量引用時,竟然拋出了npe,彷彿是由於這個靜態變量的初始化在靜態方法被調用時,尚未觸發,從而致使這個問題,所以今天專門來學習下靜態成員的初始化順序,以及上面這個問題致使的緣由java
類的初始化順序ide
靜態變量, 靜態代碼快 -》 實例變量(屬性,實例代碼塊,構造方法)post
繼承關係初始化順序學習
父類靜態成員,靜態代碼塊 -》 子類靜態成員,靜態代碼塊 -》 父類實例變量(屬性,實例代碼塊,構造方法)-》子類實例變量(屬性,實例代碼塊,構造方法)測試
類初始化時,會優先初始化靜態成員,那麼一個類中有多個靜態成員時,如何處理的?idea
下面是一個使用靜態成員,靜態代碼塊,靜態方法的測試類,那麼下面的輸出應該是怎樣的呢?debug
public class StaticTest { static class A { public A(int i) { System.out.println("a init! " + i); } } static class B { public B(int i) { System.out.println("b init! " + i); } } private static A a1 = new A(1); private static B b1; private static int num; private static B b2 = new B(2); private static A a2 = genA(2); static { b1 = new B(1); } private static A genA(int i) { System.out.println("gen A: " + i); return new A(i); } private static B genB(int i) { System.out.println("gen B: " + i); return new B(i); } public static void doSome() { System.out.println("static function doSome called! a3!=null : " + (a3 != null) + " | num > 0 : " + num); } private static A a3; private static B b3; static { System.out.println("num : " + num); num = 10; a3 = genA(3); b3 = genB(3); } public static void main(String[] args) { doSome(); } }
輸出以下code
a init! 1 b init! 2 gen A: 2 a init! 2 b init! 1 num : 0 gen A: 3 a init! 3 gen B: 3 b init! 3 static function doSome called! a3!=null : true | num > 0 : 10
從實際的輸出結果來看:blog
那麼問題來了,若是在某個靜態成員初始化的時候拋出了異常,會怎樣?繼承
那麼稍稍改一下上面的代碼,加一個主動拋異常的case
public class StaticTest { static class A { public A(int i) { System.out.println("a init! " + i); } } static class B { public B(int i) { System.out.println("b init! " + i); } } private static A a1 = new A(1); private static B b1; private static int num; private static B b2 = new B(2); private static A a2 = genA(2); static { b1 = new B(1); } private static A genA(int i) { System.out.println("gen A: " + i); return new A(i); } private static B genB(int i) { System.out.println("gen B: " + i); return new B(i); } private static A aError = genError(); private static A genError() { System.out.println("gen error!"); throw new RuntimeException(); // return new A(10); } public static void doSome() { System.out.println("static function doSome called! a3!=null : " + (a3 != null) + " | num > 0 : " + num); } private static A a3; private static B b3; static { System.out.println("num : " + num); num = 10; a3 = genA(3); b3 = genB(3); } public static void main(String[] args) { doSome(); } }
此時輸出:
a init! 1 b init! 2 gen A: 2 a init! 2 b init! 1 gen error! Exception in thread "main" java.lang.ExceptionInInitializerError Caused by: java.lang.RuntimeException
也就是說,初始化異常以後的代碼將不會在繼續執行
那麼第二個問題來了,前面說到哪一個問題是什麼狀況
最開始說到,在調用類的靜態方法時,發現本該被初始化的靜態成員,依然是null,從上面的分析來講,惟一的可能就是在成員變量初始化的過程當中,出現了異常
那麼,就有另外一個問題了,初始化就報錯了,這個類的靜態方法還能被調用執行麼(加入這個靜態方法不依賴內部的靜態成員)?
將前面的 genA()
方法的private去掉,改爲默認的訪問範圍,而後下面給出一個演示:
經過這個演示,也挺有意思的,第一次訪問,會拋出一個初始化異常;可是再調用一次,結果發現竟然正常執行了;可是調用public方法時,每次都是拋異常
致使這個問題的緣由,還有待考究,可是前面這個問題的答案,估摸着和下面差很少了(可是不敢肯定,有待大神指點)
測試case也比較簡單,把前面的代碼中的static去掉便可, 輸出
a init! 1 b init! 2 gen A: 2 a init! 2 b init! 1 num : 0 gen A: 3 a init! 3 gen B: 3 b init! 3 static function doSome called! a3!=null : true | num > 0 : 10
依然是根據初始化代碼的前後順序進行的
固然若是出現異常的狀況,和前面的結果相似,再也不贅述
類的初始化順序
靜態變量, 靜態代碼快 -》 實例變量(屬性,實例代碼塊,構造方法)
繼承關係初始化順序
父類靜態成員,靜態代碼塊 -》 子類靜態成員,靜態代碼塊 -》 父類實例變量(屬性,實例代碼塊,構造方法)-》子類實例變量(屬性,實例代碼塊,構造方法)
相同等級的初始化的前後順序,是直接依賴代碼中初始化的前後順序
理論上,類初始化中拋出了異常,那麼這個類將沒法被classLoader正確的加載,所以也沒法有效的使用這個類
可是不排除某些狀況下,依然強行的使用了這個類(如上面gif圖中的演示),這個原理還不太清晰,也有多是idea的debug功能有什麼黑科技?
注意
所以,請格外注意,在初始化代碼中,請確保不會有拋出異常,若是沒法把控,不妨新建一個init()
方法來實現初始化各類狀態,而後在代碼中主動調用好了
盡信書則不如,已上內容,純屬一家之言,因本人能力通常,看法不全,若有問題,歡迎批評指正