Java语言理论告诉我们内部类对象持有外部类对象的一个引用,这说明内部类与外部类还是独立的两个类,只不过内部类对象通过持有外部类的对象的引用来维持这个关系。
通常任何一个类都不可能访问另一个类的私有成员,那么内部类是如何做到访问外部类的私有成员的呢?
其实“持有外部类对象的一个引用”这句话已经给了我们提示,Java代码中并不需要自己去声明这么一个引用,因此是编译器背着我们创建了这个引用。既然编译器有这个习性,它很可能也给外部类的私有成员创建了getter方法使得内部类可以访问。
下面我们用javap命令反编译class文件来探寻这个猜想是否正确。
javap 用法:javap class文件路径 获得类摘要信息,直接输出到标准输出。
第一步,验证内部类对象持有外部类对象的引用。
先定义一个外部类和一个内部类:
class Outer {
class Inner {
}
}
使用 javac 编译出class文件,会生成多个class文件:Outer.class Outer$Inner.class。
javap Outer$Inner.class 输出:
class Outer$Inner {
final Outer this$0; // 外部类对象的引用
Outer$Inner(Outer); // 还生成了一个构造函数,传入了外部类的引用。
}
一个有趣的问题来了:如果Inner显式定义了构造函数会怎么样?
class Outer {
class Inner {
private final String name;
public Inner(String name) {
this.name = name;
}
}
}
javap Outer$Inner.class 输出:
class Outer$Inner {
final Outer this$0;
public Outer$Inner(Outer, java.lang.String); // 改造了显式定义的构造函数
第二步,验证外部类生成了私有成员变量的访问器
先添加一个私有成员变量:
class Outer {
private final int code;
public Outer(int code) {
this.code = code;
}
class Inner {
private final String name;
public Inner(String name) {
this.name = name;
}
}
}
javap输出:
class Outer {
public Outer(int);
}
可见,内部类不访问外部类私有成员变量时,并没有隐藏的方法声明。
然后,添加内部类对外部类私有成员变量的访问:
class Outer {
private final int code;
public Outer(int code) {
this.code = code;
}
class Inner {
private final String name;
public Inner(String name) {
this.name = name;
}
public void hello() {
System.out.println("Inner: hello: " + code);
}
}
}
javap Outer.class 输出:
class Outer {
public Outer(int);
static int access$000(Outer); // 多了一个静态方法,返回私有成员。
}
再添加一个私有方法:
class Outer {
private final int code;
public Outer(int code) {
this.code = code;
}
private void sayhi(String message) {
System.out.println("Outer: hi, " + message);
}
class Inner {
private final String name;
public Inner(String name) {
this.name = name;
}
public void hello() {
System.out.println("Inner: hello: " + code);
sayhi("this is from inner");
}
}
}
javap Outer.class 输出:
class Outer {
public Outer(int);
static int access$000(Outer);
static void access$100(Outer, java.lang.String);
}
结论:
内部类创建了一个外部类对象的引用,并通过改造构造函数将其传入内部类。
内部类如果不访问外部类的私有成员,并不会生成访问方法,而是需要的时候才生成。
外部类生成的访问方法,是static类型的,并传入外部类对象引用,返回值与参数根据需要访问的变量和函数相对应。