32

我不明白为什么会编译。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();
        }
    }
}
4

4 回答 4

32

(编辑:扩展答案以回答一些评论)

编译器采用内部类并将它们转换为顶级类。由于私有方法仅对内部类可用,编译器必须添加具有包级别访问权限的新“合成”方法,以便顶级类可以访问它。

像这样的东西(编译器添加的 $ ):

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 级别没有内部类之类的东西)。然后编译器还必须生成新的“转发”方法。它们是在包级别(非公共)制作的,以确保只有同一包中的类可以访问它们。编译器还将私有方法的方法调用更新为生成的“转发”方法。

您可以避免让编译器生成将方法声明为“包”的方法(缺少公共、私有和受保护的方法)。这样做的缺点是包中的任何类都可以调用方法。

编辑:

是的,您可以调用生成(合成)方法,但不要这样做!:

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);
            }
        }
    }
}
于 2009-03-19T17:24:35.420 回答
14

我认为这句话很好地总结了它:

...内部类可以访问声明类的所有成员,甚至是私有成员。实际上,内部类本身就是类的成员;因此,遵循面向对象工程的规则,它应该可以访问类的所有成员。

从那以后,由于两个内部类实际上只是包含类的一部分,因此它们也应该能够访问彼此的私有成员。

于 2009-03-19T17:09:46.407 回答
4

Java 在特殊的访问器中编译,其中包含 $。所以你不能编写访问私有方法的Java。在这里解释:

http://www.retrologic.com/innerclasses.doc7.html

还有一类编译器生成的成员。如果一个类包含另一个类,或者如果它们被一个公共类包含,则另一个类 D 可以使用类 C 的私有成员 m。由于虚拟机不知道这种分组,编译器在 C 中创建了一个本地访问方法协议,以允许 D 读取、写入或调用成员 m。这些方法的名称形式为 access$0、access$1 等。它们从不公开。访问方法的独特之处在于它们可以添加到封闭类,而不仅仅是内部类。

于 2009-03-19T17:06:38.520 回答
0

正如用户“A Dude”在已接受答案的评论中解释的那样:

它可以编译,因为语言规范要求它以这种方式工作,即。Java 语言规范是这样说的:

6.6.1 确定可访问性(至少从 JLS6 开始)

“否则,如果成员或构造函数被声明为私有,那么当且仅当它发生在包含成员或构造函数声明的顶级类(第 7.6 节)的主体内时,才允许访问。”

即私有成员的“访问范围”是:在顶级类主体的词汇边界内的任何地方。

这意味着:在最外层类的类体内定义的所有私有成员都可以从该类体内的其他任何地方访问。

例如,可以从外部类的方法或外部类的另一个内部类的任何方法访问内部类的私有方法。

于 2018-03-05T15:30:28.860 回答