为什么内部类可以访问私有方法?

时间:2009-03-19 17:01:44

标签: java inner-classes

我不明白为什么这会编译。 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 个答案:

答案 0 :(得分:28)

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

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

像这样($ 1由编译器添加):

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级别,不存在内部类)。然后编译器还必须生成新的“转发”方法。它们是在包级别(非公共)创建的,以确保只有同一个包中的类才能访问它们。编译器还将对私有方法的方法调用更新为生成的“转发”方法。

您可以避免编译器生成方法我将方法声明为“package”(缺少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);
            }
        }
    }
}

答案 1 :(得分:14)

我认为this quote总结得很好:

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

接下来,由于两个内部类实际上只是包含类的一部分,因此它们也应该能够访问其他私有成员。

答案 2 :(得分:4)

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

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

  

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

答案 3 :(得分:0)

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

它编译,因为它需要通过语言指定以这种方式工作,即。 Java Lang Spec如此说:

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

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

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

这意味着:可以从此类体中的其他任何位置访问在最外层类的类体中定义的所有私有成员。

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