我们都知道java中类的生命周期包含7个阶段:加载 –> 验证 –> 准备 –> 解析 –> 初始化 –> 使用 –> 卸载
其中 验证、准备、解析 三个阶段又统称为 连接,那么在java中类的初始化情况时什么样的???在哪些情况下会触发类的初始化,个人总结了一下些情况(个人研究,可能不全)
- 读取的 static final 不会触发类的初始化
- 读取的static 将触发的初始化
- 调用的static 方法 将触发的初始化
- new 一个类将触发类的初始化
- 使用reflect进行反射操作将触发类的初始化
- 读取子类的 static final 不会出发类的初始化
- 读取子类的static 字段将触发类的初始化,如果父类还未初始化过,将首先初始化父类,再初始化子类
- 通过reflect进行反射操作子类,将会触发类的初始化,如果父类还未初始化过,将首先初始化父类,再初始化子类
- 通过子类读取父类的static final 字段将不会触发类的初始化
- 通过子类读取父类的static字段将会触发父类的初始化,而不会触发子类的初始化
- new 子类对象时将触发类的初始化,如果父类还未初始化过,将首先初始化父类,再初始化子类
- 定义类的数组将不会触发类的初始化
- 当虚拟机启动时,将初始化化启动主类(包含有main方法的类)
- 当一个子类初始化时,要求父类先初始化
- 接口初始化与普通类的区别在于,接口初始化时并不要求全部父接口完成初始化,只有要用到时才会初始化(摘自深入java虚拟机)
关于接口的初始化:
接口的加载过程与类加载过程稍有不同,由于接口中不能使用static语句块,但是编译器任然会为接口生成“()”构造器,用于初始化接口中所有定义的成员变量,其与类的区别:当一个接口初始化时,类要求其父类全部都已经初始化了,但是在一个接口初始化时,并不要求其父类接口全部都完成完成了初始化,只有在真正是使用到父接口的时候才会初始化。
关于final static 修饰的字段:
static final 修饰的字段已在编译的时候就会初始化并存入类的常量池,在类准备阶段就直已经初始化了,所以在引用时,本质上并没有直接引用到类,而是直接对常量池引用,因此不会触发初始化
public class SubClass extends SuperClass {
static {
System.out.println("sub class init ");
}
public static String sub = "sub";
public static final String subf = "sub final";
}
public class SuperClass {
static {
System.out.println("super class init");
}
public static String sup = "super";
public static final String supf = "super final";
public static String getSup() {
return sup;
}
}
public class Main {
static {
System.out.println("main init");
}
public static void main(String[] args) throws ClassNotFoundException {
// System.out.println(SuperClass.supf);//读取的 static final 不会触发类的初始化,原因:static final 修饰的字段存
// 放在常量池;常量在编译的时候就会存入类的常量池,在类加载的时候就直接将该值存
// 入方法区的常量池中,本质上并没有直接引用到类,而是直接对常量池引用,因此不会触发初始化
// System.out.println(SuperClass.sup); //读取的static 将触发的初始化
// System.out.println(SuperClass.getSup());//调用的static 方法 将触发的初始化
// new SuperClass(); //new 一个类将触发类的初始化
// Main.class.getClass().forName(SuperClass.class.getName()); //使用reflect进行反射操作将触发类的初始化
// System.out.println(SubClass.subf); // 读取子类的 static final 不会出发类的初始化
// System.out.println(SubClass.sub); //读取子类的static 字段将触发类的初始化,如果父类还未初始化过,将首先初始化父类,再初始化子类
// Main.class.getClass().forName(SubClass.class.getName()); //通过reflect进行反射操作子类,将会触发类的初始化,
// 如果父类还未初始化过,将首先初始化父类,再初始化子类
// System.out.println(SubClass.supf); //通过子类读取父类的static final 字段将不会触发类的初始化
// System.out.println(SubClass.sup); //通过子类读取父类的static字段将会触发父类的初始化,而不会触发子类的初始化
// new SubClass(); // new 子类对象时将触发类的初始化,如果父类还未初始化过,将首先初始化父类,再初始化子类
SubClass[] subarr = new SubClass[1]; // 定义类的数组将不会触发类的初始化
}
}