在写一个通用的报警模块时,遇到一个有意思的问题,在调用静态方法时,发现静态方法内部对静态变量引用时,竟然抛出了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()
方法来实现初始化各类状态,而后在代码中主动调用好了
尽信书则不如,已上内容,纯属一家之言,因本人能力通常,看法不全,若有问题,欢迎批评指正