我不明白为什么要编译。 f() 和 g() 从内部类可见,尽管它们是私有的。是因为它们是内部类而被特殊对待吗?
如果A和B不是静态类,还是一样的
class NotPrivate {
private static class A {
private void f() {
new B().g();
}
}
private static class B {
private void g() {
new A().f();
}
}
}
(编辑:扩展回答一些评论的答案)
编译器获取内部类并将它们转换为顶级类。由于私有方法仅对内部类可用,因此编译器必须添加具有包级访问权限的新“合成”方法,以便顶级类可以访问它。
像这样的东西($是编译器添加的):
class A
{
private void f()
{
final B b;
b = new B();
// call changed by the compiler
b.$g();
}
// method generated by the compiler - visible by classes in the same package
void $f()
{
f();
}
}
class B
{
private void g()
{
final A a;
a = new A();
// call changed by the compiler
a.$f();
}
// method generated by the compiler - visible by classes in the same package
void $g()
{
g();
}
}
非静态类是一样的,但它们增加了对外部类的引用,以便可以在其上调用方法。
Java 这样做的原因是他们不想要求 VM 更改来支持内部类,因此所有更改都必须在编译器级别进行。
编译器获取内部类并将其变成顶级类(因此,在 VM 级别没有内部类这样的东西)。然后编译器还必须生成新的“转发”方法。它们是在包级别(非公共)创建的,以确保只有同一包中的类才能访问它们。编译器还将对私有方法的方法调用更新为生成的“转发”方法。
您可以避免让编译器生成方法,我将方法声明为“包”(缺少 public、private 和 protected)。缺点是包中的任何类都可以调用这些方法。
编辑:
是的,您可以调用生成的(合成的)方法,但不要这样做!:
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class Main
{
public static void main(final String[] argv)
throws Exception
{
final Class<?> clazz;
clazz = Class.forName("NotPrivate$A");
for(final Method method : clazz.getDeclaredMethods())
{
if(method.isSynthetic())
{
final Constructor constructor;
final Object instance;
constructor = clazz.getDeclaredConstructor(new Class[0]);
constructor.setAccessible(true);
instance = constructor.newInstance();
method.setAccessible(true);
method.invoke(null, instance);
}
}
}
}
我认为这句话很好地总结了它:
...内部类可以访问声明类的所有成员,甚至私有成员。实际上,内部类本身就是类的成员;因此,遵循面向对象工程的规则,它应该可以访问类的所有成员。
从那以后,由于两个内部类实际上只是包含类的一部分,因此它们也应该能够访问彼此的私有成员。
Java 在带有 $ 的特殊访问器中编译。所以你不能编写访问私有方法的 Java。在这里解释:
http://www.retrologic.com/innerclasses.doc7.html
还有一类编译器生成的成员。类 C 的私有成员 m 可以被另一个类 D 使用,如果一个类包含另一个类,或者它们包含在一个公共类中。由于虚拟机不知道这种分组,编译器在 C 中创建了一个访问方法的本地协议,以允许 D 读取、写入或调用成员 m。这些方法的名称形式为 access$0、access$1 等。它们从不公开。访问方法的独特之处在于它们可以添加到封闭类,而不仅仅是内部类。
正如用户“A Dude”在已接受答案的评论中解释的那样:
它可以编译,因为语言规范要求它以这种方式工作,即。 Java Lang 规范是这样说的:
6.6.1 确定可访问性(至少从 JLS6 开始)“否则,如果成员或构造函数被声明为私有,则当且仅当它发生在包含成员或构造函数声明的顶级类 (§7.6) 的主体内时,才允许访问。”
即私有成员的“访问范围”是:在顶级类主体的词法边界内的任何地方。
这意味着:在最外层类的类主体中定义的所有私有成员都可以从该类主体的任何其他地方访问。
例如,可以从外部类的方法或外部类的另一个内部类的任何方法访问内部类的私有方法。