之前已经对类初始化相关的东东进行了非常细致的学习,另外也对类加载器进行了初始的了解,其中对于类的主动方式会有七种,这里再来回顾一下:
![](https://img-blog.csdnimg.cn/img_convert/12cc1371ecefbd0429632dffefd9042f.png)
这次做一些实验来进一步巩固关于类初始化相关的知识点,下面开始:
实验一:
![](https://img-blog.csdnimg.cn/img_convert/1f8697664ace8445b508caac8875ba16.png)
为什么?如果彻底搞清楚了之前关于类初始化的知识点,那这个不难解释,原因是由于:FinalTest.x目前是个编译期常量,它会在编译期间将其放到常量池,并不会导到FinalTest的主动使用,为了更进一步能看清本质,咱们将MyTest8的字节码文件进行反编译,如下:
![](https://img-blog.csdnimg.cn/img_convert/abc555603db4cfa0d502ca7471953ddf.png)
好下面继续改造:
![](https://img-blog.csdnimg.cn/img_convert/f75170049ba41e692af6cb0eb2f9e815.png)
它是属于七种主动使用类的情况之一,如下:
![](https://img-blog.csdnimg.cn/img_convert/ebb1523a8be1efbd5a641807c64ef3b4.png)
接着再来修改:
![](https://img-blog.csdnimg.cn/img_convert/6ecbb6187e2d46240f4f20958ab8a18b.png)
此时继续反编译一下:
![](https://img-blog.csdnimg.cn/img_convert/62c769a11b499e966253a1db4c6e480c.png)
实验二:
![](https://img-blog.csdnimg.cn/img_convert/fb8bef71be51d6f608ea20e1647547df.png)
为什么是按这样一个顺序输出的呢?由于使用到了main()静态方法,所以会导致MyTest9的主动使用,所以"MyTes9 static block"会被初始化,其原因还是七种主动使用的情况之一,如下:
![](https://img-blog.csdnimg.cn/img_convert/7a375b598821c92cf5fabf0d3095bd1f.png)
接着由于"Child.b"子类静态变量的使用会导到它的父类进行初始化,所以"Parent static block"输出了,最后自己再初始化,所以"Child static block"输出了,最终再输出要打印的变量的值,为了进一步查看类的加载信息,还是给JVM加上"-XX:+TrancClassLoading"参数来进行观测,如下:
![](https://img-blog.csdnimg.cn/img_convert/8ef6375260f758d9702d42a25a62f8e1.png)
实验三:
package com.jvm.classloader;
class Parent2 {
static int a = 2;
static {
System.out.println("Parent2 static block");
}
}
class Child2 extends Parent2 {
static int b = 4;
static {
System.out.println("Child2 static block");
}
}
public class MyTest10 {
static {
System.out.println("MyTest10 static block");
}
public static void main(String[] args) {
Parent2 parent2;
System.out.println("------------------");
parent2 = new Parent2();
System.out.println("------------------");
System.out.println(parent2.a);
System.out.println("------------------");
System.out.println(Child2.b);
}
}
编译运行:
![](https://img-blog.csdnimg.cn/img_convert/ddec9e1ebf0c36ce58ee5ab249399061.png)
这又是为何呢?分析一下:由于调用了main()方法,则MyTest10会被初始化,所以“MyTest10 static block”被输出了,而这个不会导致任何输出:
![](https://img-blog.csdnimg.cn/img_convert/e6e1486d8b9b4fd17e404942a19e78d2.png)
接着生成Parent2的实例则会导到它被初始化,如下:
![](https://img-blog.csdnimg.cn/img_convert/7db55a530c28815b26f1c0a36ddaaa8e.png)
而它对应这种主动使用的情况:
![](https://img-blog.csdnimg.cn/img_convert/294313c45404356fb70c9c2b72828837.png)
,所以"Parent2 static block"被输出了,接着输出Parent2.a,所以“3”就被输出出来了,最后这句话会导到Child2被初始化,如下:
![](https://img-blog.csdnimg.cn/img_convert/6bf1b1817cca55ce3e472c5897320c60.png)
照理在初始化子类的时候,应该先初始化父类Parent2,但是由于Parent2已经在之前初始化过了,所以这里就不会再次初始了,接着走Child2的初始化逻辑,于是乎“Child2 static block”就被打印出来了,最终再打印Child2.b值"4"。
实验四:
![](https://img-blog.csdnimg.cn/img_convert/dacd22504b4bff0363f0173c767b3bc1.png)
下面分析一下:由于Child3.a调用的是它父类Parent3里面的变量,所以也就是对Parent3的主动使用,于是乎就会打印出:
![](https://img-blog.csdnimg.cn/img_convert/f4899a922fcc8a328d70fd5cf230fef1.png)
而接下来还是通过Child3去调用它父类的doSomething(),同样也不会导至Child3的初始化,而只会导致Parent3进行初始化,但是由于它之前已经被初始化了,所以输出为:
![](https://img-blog.csdnimg.cn/img_convert/9fb927c22f7be6af6c9fbd9cb446eaa8.png)
所以总结一下:如果用子类去访问父类的静态变量或静态方法,表示的是对于父类的主动使用,而非表示对子类的主动使用。
实验五:
这个实现会涉及到类加载器相关的东东,但是还是说明类初始化相关的知识点,具体如下:
![](https://img-blog.csdnimg.cn/img_convert/ddfbd44f5ff6d78c5a922e819fd004be.png)
其中可以发现通过系统加载类时是不会导致类的初始化的,而通过Class.forName是会导致类的初始化,其它这也对应七种主动使用的这种情况:
![](https://img-blog.csdnimg.cn/img_convert/47cf42340a137d6cb3c0feae3278c623.png)
所以总结一下:调用ClassLoader类的loadClass方法加载一个类,并不是对类的主动使用,也就不会导致类的初始化。