之前逛帖子时看到了这么一种说法,外部类先于内部类加载,抱着好奇的心态,尝试了一下反编译,发现并不是如此,内部类比外部类先加载,只是外部类先执行构造方法,接着将this指针传给内部类的构造函数。
实验代码如下:
class deal
{
class deal1
{
}
}
public class Try {
public static void main(String[] args) {
deal.deal1 n=new deal().new deal1();
}
}
反编译Try.class文件后:
Last modified 2018-1-29; size 410 bytes
MD5 checksum 42a56edb44f2c568af2ca2a51a2cb875
Compiled from "Try.java"
public class Try
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #8.#17 // java/lang/Object."<init>":()V
#2 = Class #18 // deal$deal1
#3 = Class #21 // deal
#4 = Methodref #3.#17 // deal."<init>":()V
#5 = Methodref #8.#22 // java/lang/Object.getClass:()Ljava/lang/Class;
#6 = Methodref #2.#23 // deal$deal1."<init>":(Ldeal;)V
#7 = Class #24 // Try
#8 = Class #25 // java/lang/Object
#9 = Utf8 <init>
#10 = Utf8 ()V
#11 = Utf8 Code
#12 = Utf8 LineNumberTable
#13 = Utf8 main
#14 = Utf8 ([Ljava/lang/String;)V
#15 = Utf8 SourceFile
#16 = Utf8 Try.java
#17 = NameAndType #9:#10 // "<init>":()V
#18 = Utf8 deal$deal1
#19 = Utf8 deal1
#20 = Utf8 InnerClasses
#21 = Utf8 deal
#22 = NameAndType #26:#27 // getClass:()Ljava/lang/Class;
#23 = NameAndType #9:#28 // "<init>":(Ldeal;)V
#24 = Utf8 Try
#25 = Utf8 java/lang/Object
#26 = Utf8 getClass
#27 = Utf8 ()Ljava/lang/Class;
#28 = Utf8 (Ldeal;)V
{
public Try();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 7: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=2, args_size=1
0: new #2 // class deal$deal1,可以看到先加载内部类,此时类加载的一套龙服务都会完成。
3: dup
4: new #3 // class deal,此处加载外部类。
7: dup
8: invokespecial #4 // Method deal."<init>":()V
11: dup
12: invokevirtual #5 // Method java/lang/Object.getClass:()Ljava/lang/Class;
15: pop
16: invokespecial #6 // Method deal$deal1."<init>":(Ldeal;)V
19: astore_1
20: return
LineNumberTable:
line 9: 0
line 10: 20
}
SourceFile: "Try.java"
InnerClasses:
#19= #2 of #3; //deal1=class deal$deal1 of class deal
可以看到,JVM先执行内部类的new指令,该指令会导致内部类被加载,完成一系列加载过程后,会在堆内存中根据方法区的类信息进行内存分配,接着加载和分配内存给外部类,然后执行外部类的<init>方法,最后执行内部类的<init>方法,传入外部类的this指针。
这段反编译代码有些地方不是很懂,中间会调用外部类的getClass方法,但返回值又没有用(调用该函数的下一条指令为pop,返回值没用过就丢弃了),以后在研究。
所以,内部类先于外部类加载。