很多人在学习内部类的时候会有这样的疑问:当一个类在加载进内存的时候里面的内部类会随着一起加载进内存吗?当一个内部类加载进内存的时候其所在的外部类会先加载进内存吗?答案都是否定的,那么接下来我们去验证并研究其中的原因。
内部类加载验证外部类是否加载。请看如下案例:
public class Demo {
public static void main(String[] args) {
A.B.C c = new A.B.C(); // 请问输出什么?
}
static class A {
static {
System.out.println("A 静态代码块");
}
static class B {
static {
System.out.println("B 静态代码块");
}
static class C {
static {
System.out.println("C 静态代码块");
}
}
}
}
}
注意这里面用到了静态代码块,静态代码块会随着类的加载而执行。以上代码:Demo类里面有静态成员内部类A,A里面有静态成员内部类B,B里面有静态成员内部类C。创建C类的对象,这句代码执行只输出了”C 静态代码块”。毋庸置疑,创建C类对象肯定会加载C类的,C类的构造代码块也会随之执行,但如果C类编译后的内容是存放在Demo.class里面的,那么要想加载C类,肯定会先加载Demo.class,其里面的A类B类C类,也都会被加载,也就是会打印”a静态代码块””b静态代码块””c静态代码块”,但是只打印了“c静态代码块”,说明C类编译后的内容并没有存放在Demo.class当中,请看下图:
内部类会被编译为独立的 class 文件,并被命名为:外部类名$内部类名.class
因此这个 Demo.java 文件编译后实际会生成四个文件:Demo.class、Demo$A.class、Demo$A$B.class、Demo$A$B$C.class,说明创建C类对象只加载Demo$A$B$C.class文件。但是疑问又来了,确实生成了四个class文件,但是C类是Demo类的内部类,Demo.class会不会也有一份C类编译后的内容呢?接下来我们去看下Demo.class和Demo$A$B$C.class的内容,请看下图:
我们发现Demo.class里面只有三个内部类的声明,并没有内部类里面的内容,比如A类,只声明了他day08包下,外部类是Demo,类名是A,是static的,但是并没有A的内容。
Demo$A$B$C.class有自身的代码内容构造方法和静态代码块,同时也有三个外部类的声明。
通过上面的内容我们得到了这样一个结论,内部类和外部类的联系是建立在其class文件里面的声明和class文件的命名的基础上的,并非建立在编译后的内容相互包含的关系上。根据这个结论我们就很容易理解内部类的加载与外部类的加载无关。
外部类加载验证内部类是否加载。根据上面的结论我们就明白了外部类加载其内部类不会随着加载的,用下面代码验证一下:
图片5.png (29.76 KB, 下载次数: 0)
下载附件 保存到相册
4 天前 上传
注意:在 new A.B.C() 的时候其实 A 类和 B 类不需要初始化,这时 A.B. 其实只是个限定而已,所以实际上只需要去初始化 Demo$A$B$C.class即可,因此只有"C静态代码块"会被执行。
之前我发表过一篇帖子“Java基础之Class的加载过程”,里面已经详细的讲过了类的加载的过程,这里咱们再复习一下:类的加载分为装载、连接、初始化。静态代码块,就是在初始化这个阶段执行的。通过下面案例来解释一下:
显然只有Class.forName()进行到了初始化这一步,而Person.class,只有装载和连接的过程。同理,new A.B.C() 的时候 A 类和 B 类也没有执行到初始化这一步。 |
|