回归Java基础:触发类加载的六大时机

前言

什么状况下会触发类加载的进行呢?本文将结合代码demo谈谈几种状况,但愿对你们有帮助。面试

类加载时机

什么状况须要开始类加载过程的第一阶段:加载?Java虚拟机规范中并无进行强制约束,这点能够交给虚拟机的具体实现来自由把握。可是对于初始化阶段,虚拟机规范则严格规定了如下几种状况必须当即对类进行初始化,若是类没有进行过初始化,则须要先触发其初始化。bash

建立类的实例

为了验证类加载,咱们先配置一个JVM参数函数

-XX:+TraceClassLoading 监控类的加载
复制代码

在IDE配置以下:学习

demo代码:ui

public class ClassLoadInstance {

    static {
        System.out.println("ClassLoadInstance类初始化时就会被执行!");
    }

    public ClassLoadInstance() {
      System.out.println("ClassLoadInstance构造函数!");
    }
}

public class ClassLoadTest {

    public static void main(String[] args) {
        ClassLoadInstance instance = new ClassLoadInstance();
    }
}

复制代码

运行结果:spa

结论:3d

new ClassLoadInstance实例时,发现ClassLoadInstance被加载了,所以 new建立实例对象,会触发类加载进行。code

访问类的静态变量

demo代码:cdn

public class ClassLoadStaticVariable {

    static {
        System.out.println("ClassLoadStaticVariable类初始化时就会被执行!");
    }

    public static int i = 100;

    public ClassLoadStaticVariable() {
      System.out.println("ClassLoadStaticVariable构造函数!");
    }

}

public class ClassLoadTest {

    public static void main(String[] args) {
        System.out.println(ClassLoadStaticVariable.i);
    }
}
复制代码

运行结果:对象

结论:

访问类ClassLoadStaticVariable的静态变量i时,发现ClassLoadStaticVariable类被加载啦,所以访问类的静态变量会触发类加载。

注意:

访问final修饰的静态变量时,不会触发类加载,由于在编译期已经将此常量放在常量池了。

访问类的静态方法

demo代码:

public class ClassLoadStaticMethod {

    static {
        System.out.println("ClassLoadStaticMethod类初始化时就会被执行!");
    }

    public static void method(){
        System.out.println("静态方法被调用");
    }

    public ClassLoadStaticMethod() {
      System.out.println("ClassLoadStaticMethod构造函数!");
    }

}

public class ClassLoadTest {

    public static void main(String[] args) {
        ClassLoadStaticMethod.method();
    }
}
复制代码

运行结果:

结论:

访问类ClassLoadStaticMethod的静态方法method时,发现ClassLoadStaticMethod类被加载啦,所以访问类的静态方法会触发类加载。

反射

demo代码:

package classload;

public class ClassLoadStaticReflect {

    static {
        System.out.println("ClassLoadStaticReflect类初始化时就会被执行!");
    }

    public static void method(){
        System.out.println("静态方法被调用");
    }

    public ClassLoadStaticReflect() {
      System.out.println("ClassLoadStaticReflect构造函数!");
    }

}

public class ClassLoadTest {

    public static void main(String[] args) throws ClassNotFoundException {
        Class.forName("classload.ClassLoadStaticReflect");
    }
}
复制代码

运行结果:

结论:

反射获得类ClassLoadStaticReflect时,发现ClassLoadStaticReflect类被加载啦,所以反射会触发类加载。

当初始化一个类时,发现其父类还未初始化,则先触发父类的初始化

demo代码:

//父类
public class ClassLoadSuper {
    static {
        System.out.println("ClassLoadSuper类初始化时就会被执行!这是父类");
    }

    public static int superNum = 100;

    public ClassLoadSuper() {
        System.out.println("父类ClassLoadSuper构造函数!");
    }
}
//子类
public class ClassLoadSub extends ClassLoadSuper {

    static {
        System.out.println("ClassLoadSub类初始化时就会被执行!这是子类");
    }

    public static int subNum = 100;

    public ClassLoadSub() {
        System.out.println("子类ClassLoadSub构造函数!");
    }

}

public class ClassLoadTest {

    public static void main(String[] args) throws ClassNotFoundException {
        ClassLoadSub classLoadSub = new ClassLoadSub();
    }
}
复制代码

运行结果:

看了运行结果,是否是发现,网上那道经典面试题( 讲讲类的实例化顺序?)也很清晰啦。 先父类静态变量/静态代码块-> 再子类静态变量/静态代码块->父类构造器->子类构造器

结论:

实例化子类ClassLoadSub的时候,发现父类ClassLoadSuper先被加载,所以当初始化一个类时,发现其父类还未初始化,则先触发父类的初始化

虚拟机启动时,定义了main()方法的那个类先初始化

demo代码:

package classload;

public class ClassLoadTest {

    public static void main(String[] args)  {
       System.out.println(ClassLoadSub.subNum);
    }
}
复制代码

运行结果:

结论:

虚拟机启动时,即便有ClassLoadSub,ClassLoadSuper,ClassLoadTest等类被加载, 但ClassLoadTest最早被加载,即定义了main()方法的那个类会先触发类加载。

练习与小结

触发类加载的六大时机,咱们都分析完啦,是否是不作个题都以为意犹未尽呢?接下来,咱们来分析类加载一道经典面试题吧。

class SingleTon {  
    private static SingleTon singleTon = new SingleTon();  
    public static int count1;  
    public static int count2 = 0;  
  
    private SingleTon() {  
        count1++;  
        count2++;  
    }  
  
    public static SingleTon getInstance() {  
        return singleTon;  
    }  
}  
  
public class ClassLoadTest {  
    public static void main(String[] args) {  
        SingleTon singleTon = SingleTon.getInstance();  
        System.out.println("count1=" + singleTon.count1);  
        System.out.println("count2=" + singleTon.count2);  
    }  
}  

复制代码

运行结果:

分析:

  1. SingleTon.getInstance(),调用静态方法,触发SingleTon类加载。
  2. SingleTon类加载初始化,按顺序初始化静态变量。
  3. 先执行private static SingleTon singleTon = new SingleTon(); ,调用构造器后,count1,count2均为1;
  4. 按顺序执行 public static int count1; 没有赋值,因此count1依旧为1;
  5. 按顺序执行 public static int count2 = 0;因此count2变为0.

我的公众号

欢迎你们关注,你们一块儿学习,一块儿讨论。

相关文章
相关标签/搜索