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

为什么外部Java类可以访问内部类私有成员?,java,class,private,inner-classes,private-members,Java,Class,Private,Inner Classes,Private Members,我观察到外部类可以访问内部类私有实例变量。这怎么可能?下面是一个示例代码,演示了相同的功能: 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 t

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

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??
    }
}

为什么允许这种行为?

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

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

  • 如果外部类中的某些功能是在单独的类中实现的,那么它将是最清晰的
  • 即使它在一个单独的类中,其功能也与外部类的工作方式密切相关
  • 根据这些要求,内部类可以完全访问外部类。由于它们基本上是外部类的成员,因此它们可以访问外部类的方法和属性(包括private)是有意义的。

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


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

    访问限制是基于每个类进行的。类中声明的方法不能访问所有实例/类成员。这很合理,内部类也可以不受限制地访问外部类的成员,而外部类可以不受限制地访问内部类的成员


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

    内部类背后的逻辑是,如果您在外部类中创建一个内部类,那是因为它们需要共享一些东西,因此,对于他们来说,能够比“常规”类拥有更多的灵活性是有道理的

    在您的例子中,如果类之间无法看到彼此的内部工作,这基本上意味着内部类可以简单地成为一个常规类,那么您可以将内部类声明为
    静态类XYZ
    。使用
    static
    将意味着它们不会共享状态(例如
    new ABC().new XYZ()
    将不起作用,您需要使用
    new ABC.XYZ()


    但是,如果是这样的话,您应该考虑一下
    XYZ
    是否真的应该是一个内部类,也许它应该有自己的文件。有时创建一个静态内部类是有意义的(例如,如果您需要一个实现外部类正在使用的接口的小型类,而这在其他任何地方都没有帮助)但是在大约一半的时间里,它应该是一个外部类。

    内部类的一个重要用例是工厂模式。 封闭类可以准备一个内部类的实例,而不受访问限制,并将该实例传递给外部世界,在外部世界中,私有访问将得到尊重

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

    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);
        }
    }
    

    如果要隐藏内部类的私有成员,可以定义与公共成员的接口,并创建实现此接口的匿名内部类。下面的示例:

    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
        }
    }
    

    另一个与此类似的问题上出现了正确答案:

    它说私人范围界定的定义是:

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

    Thilo为你的第一个问题“这怎么可能?”添加了一个好的答案。我想对第二个问题进行详细说明:为什么允许这种行为

    首先,让我们非常清楚,这种行为并不局限于内部类,根据定义,内部类是非静态嵌套类型。所有嵌套类型都允许这种行为,包括嵌套枚举和接口,这些类型必须是静态的,并且不能有封闭实例。基本上,该模型简化为lowing语句:嵌套代码可以完全访问封闭代码,反之亦然

    那么,为什么呢?我认为一个例子更能说明这一点

    想想你的身体和你的大脑。如果你把海洛因注射到手臂上,你的大脑就会兴奋起来。如果你大脑的杏仁核区域看到他认为对你个人安全的威胁,比如说黄蜂,他会让你的身体转向另一个方向,向山上跑去,而你不会三思而后行

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

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

    考虑内部类
    class Outer{
    
    private int a;
    
    class Inner{
    private int b=0;
    }
    
    void outMethod(){
    a = new Inner().b;
    }
    }