加入收藏
举报
02-03 21:58
#0
文件名称:
触发类加载的六大时机.md
所在目录:
Java基础学习
文件大小:
7.42 KB
下载地址:
whx123/JavaHome
   
免责声明:本网站仅提供指向 GitHub 上的文件的链接,所有文件的版权归原作者所有,本网站不对文件内容的合法性、准确性或安全性承担任何责任。
文本预览:
![](https://user-gold-cdn.xitu.io/2019/9/22/16d59927154281d2?w=500&h=334&f=jpeg&s=30619)
## 前言
什么情况下会触发类加载的进行呢?本文将结合代码demo谈谈几种情况,希望对大家有帮助。
## 类加载时机
什么情况需要开始类加载过程的第一阶段:加载?Java虚拟机规范中并没有进行强制约束,这点可以交给虚拟机的具体实现来自由把握。但是对于初始化阶段,虚拟机规范则严格规定了以下几种情况必须立即对类进行初始化,如果类没有进行过初始化,则需要先触发其初始化。
![](https://user-gold-cdn.xitu.io/2019/9/22/16d593cdb1dee573?w=815&h=650&f=png&s=73353)
## 创建类的实例
为了验证类加载,我们先配置一个JVM参数
```
-XX:+TraceClassLoading 监控类的加载
```
在IDE配置如下:
![](https://user-gold-cdn.xitu.io/2019/9/22/16d58ccf239f17d5?w=1269&h=840&f=png&s=96736)
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();
}
}
```
运行结果:
![](https://user-gold-cdn.xitu.io/2019/9/22/16d58d7a7735d914?w=1218&h=119&f=png&s=23340)
**结论:**
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);
}
}
```
运行结果:
![](https://user-gold-cdn.xitu.io/2019/9/22/16d58e23d0d91b82?w=1240&h=112&f=png&s=17651)
**结论:**
访问类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();
}
}
```
运行结果:
![](https://user-gold-cdn.xitu.io/2019/9/22/16d58effc2119fa4?w=1299&h=109&f=png&s=21427)
结论:
访问类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");
}
}
```
运行结果:
![](https://user-gold-cdn.xitu.io/2019/9/22/16d5900d7dfcb244?w=1227&h=82&f=png&s=17698)
**结论:**
反射得到类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();
}
}
```
运行结果:
![](https://user-gold-cdn.xitu.io/2019/9/22/16d5913a74736e7d?w=1259&h=209&f=png&s=45708)
看了运行结果,是不是发现,网上那道经典面试题(**讲讲类的实例化顺序**?)也很清晰啦。
先父类静态变量/静态代码块-> 再子类静态变量/静态代码块->父类构造器->子类构造器
**结论:**
实例化子类ClassLoadSub的时候,发现父类ClassLoadSuper先被加载,因此当初始化一个类时,发现其父类还未初始化,则先触发父类的初始化
## 虚拟机启动时,定义了main()方法的那个类先初始化
demo代码:
```
package classload;
public class ClassLoadTest {
public static void main(String[] args) {
System.out.println(ClassLoadSub.subNum);
}
}
```
运行结果:
![](https://user-gold-cdn.xitu.io/2019/9/22/16d5922bdec72ac7?w=1391&h=371&f=png&s=83213)
**结论:**
虚拟机启动时,即使有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);
}
}
```
运行结果:
![](https://user-gold-cdn.xitu.io/2019/9/22/16d592b67bfbed36?w=1332&h=268&f=png&s=50004)
**分析:**
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.
## 个人公众号
![](https://user-gold-cdn.xitu.io/2019/7/28/16c381c89b127bbb?w=344&h=344&f=jpeg&s=8943)
欢迎大家关注,大家一起学习,一起讨论。
点赞 回复
回帖
支持markdown部分语法 ?