Java类加载过程中的初始化
Java 类加载过程分为load,link(verify,prepare,resolve),initialising,use,unloading等。其中initialising阶段分为类初始化cinit和实例init的初始化。注意类初始化只会一次,实例初始化会多次。
类初始化
初始化时机
类初始化
触发类初始化的时机是调用类的静态变量和方法。如何理解?
我们知道具体触发点有:
- new 对象(对象的构造方法类似与静态方法)
- putStatic 写静态变量
- getStatic 读静态变量
- invokeStatic 调用静态方法
- 反射初始化对象 newInstance
- main方法中的类
- 初始化子类时,先初始化父类
- jdk7 动态语言?
初始化干了啥
- 在类初始化之前的准备阶段,虚拟机会将类变量(static 修饰的变量)分配内存并设置零值。
- 在类初始化阶段,执行类构造器
() 方法 - 收集所有类初始化代码和 static {} 域的代码,收集在一起成为
() 方法 - 子类初始化时会首先调用父类的
() 方法 - JVM 会保证
() 方法的线程安全,保证同一时间只有一个线程执行
- 收集所有类初始化代码和 static {} 域的代码,收集在一起成为
实例初始化
实例初始化
触发时机
new,反射初始化对象都会触发对象实例的初始化。和类的初始化有一部分重合,但是类初始化时实例不一定初始化。比如:
MyClassLoader mcl = new MyClassLoader();
// Class<?> clazz = mcl.loadClass("People"); // 类初始化
Class<?> clazz = Class.forName("People", true, mcl); // 类初始化
Object obj = clazz.newInstance(); //实例初始化
干了啥
- JVM 收集实例初始化变量和 {} 域组合成实例初始化方法
- 实例初始化时首先执行
() 方法,然后执行构造函数 - 子类通过构造函数构造实例时会首先调用父类的
() 方法和父类的构造函数,如果没有显示调用父类的构造函数,那么 JVM 会自动调用父类的无参构造函数,保证父类构造函数一定被调用,然后再是子类自己的 () 方法和构造函数;