一、背景
1.这节主要讲解什么情况下会触发类加载的进行呢,本文将结合代码demo谈谈几种情况,希望能帮助到正在努力学习的你们。
二、类加载时机
1.什么情况需要开始类加载过程的第一阶段:加载?Java虚拟机规范中并没有进行强制约束,这点可以交给虚拟机的具体实现来自由把握。但是对于初始化阶段,虚拟机规范则严格规定了以下几种情况必须立即对类进行初始化,如果类没有进行过初始化,则需要先触发其初始化。接下来我们就来分析下面的几种情况。

三、准备
1.为了验证类加载,我们先配置一个JVM参数
-XX:+TraceClassLoading 监控类的加载2.在Idea中进行配置

四、代码实例
1.第一种情况(创建类的实例)
1.1.代码
public class DemoTest {
public static void main(String[] args) {
ClassLoadInstance instance = new ClassLoadInstance();
}
public static class ClassLoadInstance {
static {
System.out.println("ClassLoadInstance类初始化就会被执行!!!");
}
public ClassLoadInstance(){
System.out.println("ClassLoadInstance的构造函数!!!");
}
}
}1.2.结果

1.3.分析
new ClassLoadInstance实例时,发现ClassLoadInstance被加载了,因此 new创建实例对象,会触发类加载进行。
2.第二种情况(访问类的静态变量)
2.1.代码
public class DemoTest {
public static void main(String[] args) {
System.out.println(ClassLoadStaticVariable.i);
}
public static class ClassLoadStaticVariable {
static {
System.out.println("ClassLoadStaticVariable类初始化就会被执行!!!");
}
public static int i = 100;
public ClassLoadStaticVariable() {
System.out.println("ClassLoadStaticVariable的构造函数!!!");
}
}
}2.2.结果

2.3.分析
访问类ClassLoadStaticVariable的静态变量i时,发现ClassLoadStaticVariable类被加载啦,因此访问类的静态变量会触发类加载。
注意:
访问final修饰的静态变量时,不会触发类加载,因为在编译期已经将此常量放在常量池了。
3.第三种情况(访问类的静态方法)
3.1.代码
public class DemoTest {
public static void main(String[] args) {
ClassLoadStaticMethod.method();
}
public static class ClassLoadStaticMethod {
static {
System.out.println("ClassLoadStaticMethod类初始化就会被执行!!!");
}
public static void method(){
System.out.println("静态方法被调用");
}
public ClassLoadStaticMethod() {
System.out.println("ClassLoadStaticMethod的构造函数!!!");
}
}
}3.2.结果

3.3.分析
访问类ClassLoadStaticMethod的静态方法method时,发现ClassLoadStaticMethod类被加载啦,因此访问类的静态方法会触发类加载。
4.第四种情况(反射)
4.1.代码
public class DemoTest {
public static void main(String[] args) throws ClassNotFoundException {
Class.forName("com.tydic.encryptiondemo.controller.DemoTest$ClassLoadStaticReflect");
}
public static class ClassLoadStaticReflect {
static {
System.out.println("ClassLoadStaticReflect类初始化就会被执行!!!");
}
public static void method() {
System.out.println("静态方法被调用");
}
public ClassLoadStaticReflect() {
System.out.println("ClassLoadStaticReflect的构造函数!!!");
}
}
}4.2.结果

4.3.分析
反射得到类ClassLoadStaticReflect时,发现ClassLoadStaticReflect类被加载啦,因此反射会触发类加载。
5.第五种情况(当初始化一个类时,发现其父类还未初始化,则先触发父类的初始化)
5.1.代码
public class DemoTest {
public static void main(String[] args){
ClassLoadSub classLoadSub = new ClassLoadSub();
}
public static class ClassLoadSuper {
static {
System.out.println("ClassLoadSuper类初始化就会被执行!!!");
}
public static int superNum =100;
public ClassLoadSuper() {
System.out.println("ClassLoadSuper的构造函数!!!");
}
}
public static class ClassLoadSub extends ClassLoadSuper {
static {
System.out.println("ClassLoadSub类初始化就会被执行!!!");
}
public static int subNum =100;
public ClassLoadSub() {
System.out.println("ClassLoadSub的构造函数!!!");
}
}
}5.2.结果

5.3.分析
看了运行结果,是不是发现,网上那道经典面试题(讲讲类的实例化顺序?)也很清晰啦。 先父类静态变量/静态代码块-> 再子类静态变量/静态代码块->父类构造器->子类构造器结论:
实例化子类ClassLoadSub的时候,发现父类ClassLoadSuper先被加载,因此当初始化一个类时,发现其父类还未初始化,则先触发父类的初始化
6.第六种情况(虚拟机启动时,定义了main()方法的那个类先初始化)
6.1.代码
public class DemoTest {
public static void main(String[] args){
ClassLoadSub classLoadSub = new ClassLoadSub();
System.out.println(classLoadSub.subNum);
}
public static class ClassLoadSub {
static {
System.out.println("ClassLoadSub类初始化就会被执行!!!");
}
public static int subNum =100;
public ClassLoadSub() {
System.out.println("ClassLoadSub的构造函数!!!");
}
}
}6.2.结果

6.3.分析
虚拟机启动时,即使有ClassLoadSub,ClassLoadSuper,ClassLoadTest等类被加载, 但ClassLoadTest最先被加载,即定义了main()方法的那个类会先触发类加载。
五、结束
就先写到这里吧!!!
















