1、非静态内部类对象持有外部的引用:
public class ExternalClass{
class InsideClass{
}
ExternalClass(){
InsideClass inside = new InsideClass();
}
}
javac 会编译出两个ExternalClass$InsideClass.class 与ExternalClass.class
使用javap -c 查看这两个文件:
public class ExternalClass {
ExternalClass();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: new #2 // class ExternalClass$InsideClass
7: dup
8: aload_0
9: invokespecial #3 // Method ExternalClass$InsideClass."<init>":(LExternalClass;)V
12: astore_1
13: return
}
ExternalClass.class 可以看出在
aload_0先将自己的引用this,压入栈中。
然后调用了一个内部类带参数的构造方法,将this 传了过去。
Compiled from "ExternalClass.java"
class ExternalClass$InsideClass {
final ExternalClass this$0;
ExternalClass$InsideClass(ExternalClass);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:LExternalClass;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: return
}
ExternalClass
InsideClass.class可以看出与源码的区别,多了一个final的this
0 全局变量, 并且jvm 自动生成一个 带有参数的构造方法,
0: aload_0 取出局部变量表中的首位(默认是当前对象this),压入操作数栈中
1: aload_1 取出局部变量表中的第二位(构造方法中的:ExternalClass 对象), 压入操作数栈中。
2:putfield #1 给#1的赋值指令,结合操作数栈中的值,解释为:取出 操作数栈栈顶的值(aload_1 放入的),赋值给 操作数栈中对象(aload_0放入的),的#1 变量中
ps:备注静态的内部类,是没有this$0,不持有外部类对象引用
内部类访问局部变量,局部变量要定义为final
源码为:
public class ExternalClass{
interface InsideClass {
public void test();
}
ExternalClass(final int i){
new InsideClass(){
@Override
public void test(){
int a = i;
}
};
}
}
ExternalClass 的字节码为:
Compiled from "ExternalClass.java"
public class ExternalClass {
ExternalClass(int);
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: new #2 // class ExternalClass$1
7: dup
8: aload_0
9: iload_1
10: invokespecial #3 // Method ExternalClass$1."<init>":(LExternalClass;I)V
13: pop
14: return
}
从ExternalClass 首先可以看到 匿名内部类的构造方法是有两个传参(ExternalClass, int)
aload_0 将局部变量表中的第0 位(this),压入操作数栈中。
iload_1 将局部变量表中的第1位(即参数int),压入操作数栈中。
在看ExternalClass$1.class
Compiled from "ExternalClass.java"
class ExternalClass$1 implements ExternalClass$InsideClass {
final int val$i;
final ExternalClass this$0;
ExternalClass$1(ExternalClass, int);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:LExternalClass;
5: aload_0
6: iload_2
7: putfield #2 // Field val$i:I
10: aload_0
11: invokespecial #3 // Method java/lang/Object."<init>":()V
14: return
public void test();
Code:
。。。。
}
ExternalClass
1是在构造方法中,将传参ExternalClass给全局变量this
0;
将传参 int 给 全局变量val$i;
到此在匿名内部类中 有一个 int 变量
外部类的方法中有一个int 变量
为了保持这两个值的一致性,需要同时定义为final
final 变量的初始化
众说周知,final 变量只能在
1、定义的时候赋值
2、构造方法中赋值
3、构造代码块 中赋值
为什么只有这三处可以赋值呢? 与普通变量与静态变量的区别是什么?
public class Test {
public final int a = 100;
public final int b;
public int c = 6;
public static final int d = 0;
{
b = 0;
}
}
javap 反编译后 对应的结果为:
public final int a;
descriptor: I //变量类型为int
flags: ACC_PUBLIC, ACC_FINAL //变量是一个public final 类型的
ConstantValue: int 100 //设置此final 值为 100
public final int b;
descriptor: I
flags: ACC_PUBLIC, ACC_FINAL
public int c;
descriptor: I
flags: ACC_PUBLIC
public static final int d;
descriptor: I
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
ConstantValue: int 0 //设置此 静态 final 字段的值为 0
public Test();
descriptor: ()V
flags: ACC_PUBLIC
Code: //除了static final 变量d, 其他变量实际进行赋值的地方。
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: bipush 100 //将100 放入栈顶
7: putfield #2 // Field a:I 将栈顶数据(100)取出,赋值给 #2 即a
10: aload_0
11: bipush 6 //将 6放入栈顶
13: putfield #3 // Field c:I c = 6
16: aload_0
17: iconst_0 // 将0 放入栈顶
18: putfield #4 // Field b:I b = 0
21: return
LineNumberTable:
line 1: 0
line 2: 4
line 4: 10
line 7: 16
line 8: 21
从字节码可以看出:
1、static final && final变量是在 定义时真正去赋值的
2、final 与其他的普通变量 实际上会在 构造方法中再次进行赋值
3、构造代码块中的代码实际是在构造方法中执行的
ps:
static 普通的静态变量,会在静态代码块中执行,例如:
public static int c = 6;
实际上的初始化位置为:
“`
public static int c;
descriptor: I
flags: ACC_PUBLIC, ACC_STATIC
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: bipush 6 //将6 放入栈中
2: putstatic #4 // Field c:I 将6赋值给c
5: return
LineNumberTable:
line 4: 0
“`