静态代码为什么先于非静态代码
这是因为静态代码是在类加载完毕后执行的,而加载类的顺序是先父类后子类,所以静态代码的执行是先执行父类的,然后执行子类的。
对于非静态变量以及实例初始化块都是在构造函数里的代码执行前执行。
所以静态代码是在类加载后执行,而实例代码是在构造函数执行前执行。
但是当我们显示控制类加载的时候情况有点变化,显示加载可以有关两种方法:
第一种:利用forName方法
当我们查API文档就会发现forName方法有两种形式。
分别如下: public static Class<?> forName(String className) throws ClassNotFoundException
public static Class<?> forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException
第二个方法值得注意的就是第二个参数boolean initialize,如果我们把这个参数设置为false,那么当我们加载完类后就不会执行静态代码和静态的初始化动作。
只有当我们new一个对象的时候才会初始化。而第三个参数是用来指明类的加载器的。
如果查看java.lang.Class类的源代码,上述两种方法最终都会调用Class类中的私有的native方法forName0(),此方法的声明如下:
private static native Class forName0(String name, boolean init , ClassLoader loader) throws ClassNotFoundException;
所以当我们调用Class.forName(name )时,其实是在方法内部调用了: forName0(name, true, ClassLoader.getCallerClassLoader());
当我们调用Class.forName(name, initialize, loader )的时候,实际上此方法内部调用了: forName0(name, initialize, loader);
第二种:利用Class对象获取的ClassLoader装载。 此方法也是在实例化时才执行静态代码的执行。
综上所述可以总结如下: 1 对于隐式的加载(new一个对象和调用类的静态方法),静态代码是在类加载后立刻执行,而对于显示加载(第一种是用java.lang.Class的forName(String str)方法,第二种是用java.lang.ClassLoader的loadClass())就如同我上面所说,加载过程是可以由我们来控制的。 2 实例化代码执行是载构造函数执行之前,涉及到继承时,父类的构造函数执行之前执行父类里的实例化代码,子类的构造函数执行之前执行子类的实例化代码。所以这样可以保证子类中用到的变量都是已经经过父类初始化的,从而保证了初始化的正确性。