一、类的初始化
主动引用:比较常见的场景有
1、使用new关键字去实例化一个对象,读取或者设置一个类的静态字段(被final修饰的静态常量除外,这样的字段会在编译阶段被放入常量池中,不会触发类的加载),调用一个类的静态方法。
2、使用java.lang.reflect的方法对类进行反射调用的时候,如果类没有被初始化,则会初始化这个类
3、当初始化一个子类的时候,如果其父类还没有进行初始化,则要先触发其父类的初始化。
4、虚拟机启动时会初始化程序的入口类(比如包含main方法的那个类)
以上的四种情形都是对类的主动引用,会触发类的初始化。
被动引用:
1、子类引用父类的静态变量,不会触发子类的初始化。对于一个静态变量,只有直接定义这个字段的类才会被初始化。
class Parent { static { System.out.println("parent init!"); } protected static String val = "jvm"; } class Child extends Parent{ static { System.out.println("child init"); } } public class InitDemo{ public static void main(String[] args) { System.out.println(Child.val); } }
结果为: parent init! jvm
子类没有被初始化
2、常量在编译阶段会被放入到调用类的常量池中,本质上没有直接引用到定义常量的类,不会触发定义常量的类的初始化
class Normal{ public final static String str = "jvm"; static { System.out.println("Normal init"); } } public class ConstantDemo { public static void main(String[] args) { System.out.println(Normal.str); } }
结果为:
jvm
Normal 类并没有被初始化
上面的代码在执行过程中并没有输出Normal init,虽然ConstantDemo 在源码中引用了Normal类的常量str,但是在编译阶段此常量的值"jvm"存储到了ConstantDemo的常量池中,对常量Normal.str的引用转化成ConstantDemo对于自身常量池的引用了,因此不会触发Normal 类的初始化。