为什么外部Java类可以访问内部类私有成员?

Why can outer Java classes access inner class private members?

我注意到外部类可以访问内部类私有实例变量。这怎么可能?下面是一个示例代码,演示了相同的内容:

1
2
3
4
5
6
7
8
9
10
class ABC{
    class XYZ{
        private int x=10;
    }

    public static void main(String... args){
        ABC.XYZ xx = new ABC().new XYZ();
        System.out.println("Hello ::"+xx.x); ///Why is this allowed??
    }
}

为什么允许这种行为?


内部类只是一种将真正属于原始外部类的一些功能清晰地分离出来的方法。当您有两个要求时,可以使用它们:

  • 如果在单独的类中实现,那么您的外部类中的一些功能将是最清晰的。
  • 即使它在一个单独的类中,功能性也与外部类的工作方式密切相关。
  • 考虑到这些要求,内部类可以完全访问其外部类。因为他们基本上是外部类的成员,所以他们有权访问外部类的方法和属性——包括privates——这是有道理的。


    如果希望隐藏内部类的私有成员,可以定义一个与公共成员的接口,并创建一个实现此接口的匿名内部类。贝娄:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class ABC{
        private interface MyInterface{
             void printInt();
        }

        private static MyInterface mMember = new MyInterface(){
            private int x=10;

            public void printInt(){
                System.out.println(String.valueOf(x));
            }
        };

        public static void main(String... args){
            System.out.println("Hello ::"+mMember.x); ///not allowed
            mMember.printInt(); // allowed
        }
    }


    内部类(为了访问控制)被认为是包含类的一部分。这意味着可以完全进入所有的私人空间。

    实现方法是使用合成的包保护方法:内部类将编译为同一包中的一个单独的类(abc$xyz)。JVM不直接支持这种级别的隔离,因此在字节码级别ABC$XYZ将具有包保护方法,外部类使用这些方法访问私有方法/字段。


    与此类似的另一个问题出现了正确答案:为什么嵌套类的私有成员可以被封闭类的方法访问?

    它说,在JLS上有一个私有范围的定义-决定可访问性:

    Otherwise, if the member or constructor is declared private, then access is permitted if and only if it occurs within the body of the top level class (§7.6) that encloses the declaration of the member or constructor.


    内部类的一个重要用例是工厂模式。封闭类可以准备一个不带访问限制的内部类实例,并将该实例传递给外部世界,在那里将允许私有访问。

    与abyx相反,声明类static不会更改对封闭类的访问限制,如下所示。同样,同一封闭类中静态类之间的访问限制也在起作用。我很惊讶…

    1
    2
    3
    4
    5
    6
    7
    8
    class MyPrivates {
        static class Inner1 { private int test1 = 2; }
        static class Inner2 { private int test2 = new Inner1().test1; }

        public static void main(String[] args) {
            System.out.println("Inner :"+new Inner2().test2);
        }
    }


    内部类背后的逻辑是,如果您在外部类中创建一个内部类,那是因为它们需要共享一些东西,因此它们能够比"常规"类拥有更多的灵活性是有意义的。

    在您的例子中,如果类能够看到彼此的内部工作是没有意义的——这基本上意味着内部类可以简单地被设置为常规类,那么您可以将内部类声明为static class XYZ。使用static意味着它们不会共享状态(例如,new ABC().new XYZ()不起作用,您需要使用new ABC.XYZ()。但是,如果是这样的话,你应该考虑一下,XYZ是否真的应该是一个内部阶级,也许它应该有自己的文件。有时,创建一个静态的内部类是有意义的(例如,如果您需要一个实现外部类正在使用的接口的小类,而这在其他任何地方都没有帮助)。但在大约一半的时间里,它本应成为一个外层阶级。


    访问限制是按类进行的。类中声明的方法无法访问所有实例/类成员。这就说明,内部阶级也可以自由地接触到外部阶级的成员,而外部阶级则可以自由地接触到内部阶级的成员。

    通过将一个类放在另一个类中,您可以使它与实现紧密相连,并且作为实现一部分的任何内容都应该可以访问其他部分。


    蒂洛为你的第一个问题补充了一个很好的答案:"这怎么可能?".我想详细说明第二个问题:为什么允许这种行为?

    对于初学者,让我们非常清楚,这种行为不仅允许内部类(根据定义是非静态嵌套类型)使用。所有嵌套类型都允许此行为,包括嵌套枚举和必须是静态的且不能有封闭实例的接口。基本上,模型简化为以下语句:嵌套代码可以完全访问封闭代码,反之亦然。

    那么,为什么呢?我认为一个例子更好地说明了这一点。

    想想你的身体和大脑。如果你往手臂里注射海洛因,你的大脑就会兴奋起来。例如,黄蜂会说,如果你大脑的杏仁核区域看到他认为对你个人安全构成威胁的东西,他会让你的身体转向另一个方向,然后跑向山丘,而你却不会"三思而后行"。

    所以,大脑是身体的一个固有部分——奇怪的是,反过来也是。在这些密切相关的实体之间使用访问控制将丧失他们的关系主张。如果您确实需要访问控制,那么您需要将类更多地分为真正不同的单元。在此之前,它们是同一个单元。进一步研究的一个驱动示例是查看Java EDCOX1 0通常是如何实现的。

    从封闭代码到嵌套代码的无限制访问在很大程度上使其无法向嵌套类型的字段和方法添加访问修饰符。这样做会增加混乱,并可能为Java编程语言的新成员提供错误的安全感。


    内部类被认为是外部类的一个属性。因此,无论内部类实例变量是否私有,外部类都可以像访问其其他私有属性(变量)一样毫无问题地进行访问。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Outer{

    private int a;

    class Inner{
    private int b=0;
    }

    void outMethod(){
    a = new Inner().b;
    }
    }

    因为您的main()方法在ABC类中,它可以访问自己的内部类。