Java 内部类 (inner class) 为何可以访问宿主类的成员(第二部分)
背景
在 [Java] 内部类 (inner class) 为何可以访问宿主类的成员 (第一部分) 中已经提到,内部类 (inner class) 会持有所在宿主类一个实例的引用,所以内部类可以访问对应的宿主类。但是还有一个问题,宿主类和内部类似乎可以互相访问对方的 private 字段/方法,这会违反 java 的 access control 的机制吗?
结论
- Java Virtual Machine Specification 中的 5.4.4. Access Control 提到宿主类和内部类能互相访问对方的
private成员。 - 虚拟机通过读取
NestMembers/NestHost属性,就可以知道宿主类和内部类之间的关系,从而允许它们访问对方的private成员。- 宿主类的
class文件中会有NestMembers属性 - 内部类的
class文件中会有NestHost属性
- 宿主类的
代码
Java Virtual Machine Specification 中的 5.4.4. Access Control有下内容
A nest is a set of classes and interfaces that allow mutual access to their
privatemembers. One of the classes or interfaces is the nest host. It enumerates the classes and interfaces which belong to the nest, using theNestMembersattribute (§4.7.29). Each of them in turn designates it as the nest host, using theNestHostattribute (§4.7.28). A class or interface which lacks aNestHostattribute belongs to the nest hosted by itself; if it also lacks aNestMembersattribute, then this nest is a singleton consisting only of the class or interface itself.
从这段文字可以看到,java 语言要求宿主类和内部类能互相访问对方的 private 成员,且相关信息保存在 NestMembers 属性与 NestHost 属性中。
我们用如下代码来进行验证(请将代码保存为 MyOuter4.java)。
public class MyOuter4 {
private int a;
// 1. a member class
class MemberClass {
private int b = a;
}
void f() {
// 2. a local class
class LocalClass {
private int c = a;
}
// 3. an anonymous class
Runnable runnable = new Runnable() {
@Override
public void run() {
int d = a;
}
};
}
}
这段代码中有一个宿主类 MyOuter4,以及如下三个内部类(代码中有对应的注释)。
三个内部类都访问了宿主类的 private 字段 a(严谨起见,应该也写点代码让宿主类访问内部类的 private 成员,这里略)。
我们用以下命令对其进行编译。
javac -g -parameters MyOuter4.java
编译后会得到如下 4 个 class 文件。
MyOuter4.class
MyOuter4$1.class
MyOuter4$1LocalClass.class
MyOuter4$MemberClass.class
用以下命令可以查看 MyOuter4.class 中的内容
javap -v -p MyOuter4
完整的内容有点长,读者可以自行查看完整结果。
注意到,结果中确实有 NestMembers 属性,相关内容如下所示。
NestMembers:
MyOuter4$MemberClass
MyOuter4$1
MyOuter4$1LocalClass
用以下命令分别可以查看 MyOuter4$1.class,MyOuter4$1LocalClass.class,MyOuter4$MemberClass.class 中的内容
javap -v -p 'MyOuter4$1'
javap -v -p 'MyOuter4$1LocalClass'
javap -v -p 'MyOuter4$MemberClass'
它们的结果中都有如下的内容。(也就是 NestHost 属性部分)
NestHost: class MyOuter4
至此,我们已经验证了
- 宿主类的
class文件中会有NestMembers属性指明对应的内部类 - 内部类的
class文件中会有NestHost属性指明对应的宿主类
至于为何宿主类和内部类都要指定对方,我觉得是出于安全的考虑。 我们以生活中的例子来进行类比。
- 某人自称是英国王室某成员的私生子/私生女,但是英国王室从不回应。
- 张三声称曾借给李四一些钱,李四否认有借钱的事情。
- A自称曾拜B为师,B未承认。
由此可见,如果当事双方只有一方认定某事曾发生过,则此事真伪存疑。所以需要当事双方共同认可。 回到宿主类/内部类的情况,那么就是说宿主类和内部类要互相承认对方。
如果本文对您有帮助,可以继续阅读 [Java] 内部类 (inner class) 为何可以访问宿主类的成员 (第三部分)。