关于内部类是如何获取外部类的private属性
上一段代码:
public class OuterClass {
private static String language = "en";
private String region = "US";
private String li;
private void kk() {
System.out.println("kk");
}
public class InnerClass {
public void printOuterClassPrivateFields() {
String fields = "language=" + language +region;
System.out.println(fields);
kk();
}
}
public static void main(String[] args) {
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.new InnerClass();
inner.printOuterClassPrivateFields();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
jdk1.8.0编译
反编译OuterClass:
public class OuterClass
{
private static String language = "en";
private String region = "US";
private String li;
private void kk()
{
System.out.println("kk");
}
public class InnerClass
{
public InnerClass() {}
public void printOuterClassPrivateFields()
{
String fields = "language=" + OuterClass.language + OuterClass.this.region;
System.out.println(fields);
OuterClass.this.kk();
}
}
public static void main(String[] args)
{
OuterClass outer = new OuterClass();
OuterClass tmp13_12 = outer;
tmp13_12.getClass();
InnerClass inner = new InnerClass(tmp13_12);
inner.printOuterClassPrivateFields();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
这是OuterClass$InnerClass的反编译:
public class OuterClass$InnerClass
{
public OuterClass$InnerClass(OuterClass paramOuterClass) {}
public void printOuterClassPrivateFields()
{
String fields = "language=" + OuterClass.access$0() + OuterClass.access$1(**this.this$0**);
System.out.println(fields);
OuterClass.access$2(this.this$0);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
我们可以看到OuterClass里面多了几个形如access$X()的静态方法
于是使用”javap -c “更深一步的反编译:
Compiled from "OuterClass.java"
public class a.OuterClass {
static {};
Code:
0: ldc #12 // String en
2: putstatic #14 // Field language:Ljava/lang/String;
5: return
public a.OuterClass();
Code:
0: aload_0
1: invokespecial #19 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #21 // String US
7: putfield #23 // Field region:Ljava/lang/String;
10: return
public static void main(java.lang.String[]);
Code:
0: new #1 // class a/OuterClass
3: dup
4: invokespecial #43 // Method "<init>":()V
7: astore_1
8: new #44 // class a/OuterClass$InnerClass
11: dup
12: aload_1
13: dup
14: invokevirtual #46 // Method java/lang/Object.getClass:()Ljava/lang/Class;
17: pop
18: invokespecial #50 // Method a/OuterClass$InnerClass."<init>":(La/OuterClass;)V
21: astore_2
22: aload_2
23: invokevirtual #53 // Method a/OuterClass$InnerClass.printOuterClassPrivateFields:()V
26: return
static java.lang.String access$0();
Code:
0: getstatic #14 // **Field language:Ljava/lang/String;**
3: areturn
static java.lang.String access$1(a.OuterClass);
Code:
0: aload_0
1: getfield #23 // **Field region:Ljava/lang/String;**
4: areturn
static void access$2(a.OuterClass);
Code:
0: aload_0
1: invokespecial #66 // **Method kk:()V**
4: return
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
这些代码都不好看,直接看注释好了。
我们可以看到,编译器帮我们生成了三个方法:access0(),access0(),access1(),access2()。从注释里可以看到,这三个方法的作用依次是返回language字段,返回region字段,调用kk()方法。(附:我才发现,编译器会将所有方法更名形如access2()。从注释里可以看到,这三个方法的作用依次是返回language字段,返回region字段,调用kk()方法。(附:我才发现,编译器会将所有方法更名形如accessX的名字)
总结:
1、当内部类调用外部类的私有属性(包括变量和方法)时,其真正的执行是调用了编译器生成的属性的静态方法(即acess0,access0,access1等)来获取这些属性值或调用方法。
(附:外部类访问内部类的private属性也是一样的)
(再附:其中java官方文档 有这样一句话
if the member or constructor is declared private, then access is
permitted if and only if it occurs within the body of the top level
class (§7.6) that encloses the declaration of the member or
constructor.
意思是 如果(内部类的)成员和构造方法设定成了私有修饰符,当且仅当其外部类访问时是允许的。)
2、编译器编译的时候,在内部类中,会生成一个this$0的变量,这个变量应该就是外部类的实例 (附:为什么非静态内部类有可能造成内存泄漏:因为非静态内部类会隐式持有外部类实例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有。)