什么情况下会触发类加载的进行呢?本文将结合代码demo谈谈几种情况,希望对大家有帮助。
什么情况需要开始类加载过程的第一阶段:加载?Java虚拟机规范中并没有进行强制约束,这点可以交给虚拟机的具体实现来自由把握。但是对于初始化阶段,虚拟机规范则严格规定了以下几种情况必须立即对类进行初始化,如果类没有进行过初始化,则需要先触发其初始化。
为了验证类加载,我们先配置一个JVM参数
-XX:+TraceClassLoading 监控类的加载 复制代码
在IDE配置如下:
demo代码:
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(); } } 复制代码
运行结果:
new ClassLoadInstance实例时,发现ClassLoadInstance被加载了,因此 new创建实例对象,会触发类加载进行。
demo代码:
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先被加载,因此当初始化一个类时,发现其父类还未初始化,则先触发父类的初始化
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); } } 复制代码
运行结果: