我观察到外部类可以访问内部类的私有实例变量。这怎么可能?下面是演示相同内容的示例代码:
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类中,能访问它自己的内部类。
main()
ABC
内部类只是一种干净地分离一些真正属于原始外部类的功能的方法。当您有两个要求时,可以使用它们:
根据这些要求,内部类可以完全访问其外部类。因为它们基本上是外部类的成员,所以它们可以访问外部类的方法和属性(包括私有属性)是有意义的。
内部类(出于访问控制的目的)被视为包含类的一部分。这意味着可以完全访问所有私处。
实现的方法是使用合成包保护方法:内部类将被编译为同一包(ABC$XYZ)中的一个单独的类。JVM不直接支持这种级别的隔离,因此在字节码级别,ABC$XYZ将具有受包保护的方法,外部类使用这些方法来访问私有方法/字段。
访问限制是在每个类的基础上进行的。在类中声明的方法无法访问所有实例/类成员。显然,内部类也可以不受限制地访问外部类的成员,外部类也可以不受限制地访问内部类的成员。
通过将一个类放在另一个类中,您可以使它与实现紧密联系在一起,并且实现的任何部分都应该可以访问其他部分。
内部类背后的逻辑是,如果您在外部类中创建一个内部类,这是因为它们需要共享一些东西,因此它们能够比“常规”类具有更多的灵活性是有意义的。
如果类无法看到彼此的内部工作方式(这基本上意味着内部类可以简单地成为常规类),则可以将内部类声明为static class XYZ。使用static将意味着它们将不共享状态(例如,new ABC().new XYZ()将不起作用,并且您将需要使用new ABC.XYZ()。 但是,如果是这样的话,您应该考虑一下XYZ是否真的应该是一个内部类,并且它是否应该拥有自己的文件。有时,创建一个静态内部类是有意义的(例如,如果您需要一个小类来实现您的外部类正在使用的接口,而这在其他任何地方都没有帮助)。但在大约一半的时间里,它应该成为一个外类。
static class XYZ
static
new ABC().new XYZ()
new ABC.XYZ()
XYZ
一个重要用例是工厂模式。 封闭类可以准备没有访问限制的内部类的实例,并将该实例传递给外部世界,在那里私有访问将得到尊重。
与阿比克斯相矛盾的是,将类声明为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 } }
有一个正确答案出现在另一个类似的问题上: 为什么嵌套类的私有成员可以被封闭类的方法访问?
它说在__上有私有作用域的定义ABC0:
否则,如果成员或构造函数被声明为private,则则当且仅当访问发生在包含成员或构造函数声明的顶级类(§7.6)的主体中时,才允许访问。
Thilo为您的第一个问题“这怎么可能?”添加了一个很好的回答。我想就第二个问题稍作阐述:为什么允许这种行为?
对于初学者来说,让我们非常清楚,这种行为并不局限于内部类,根据定义,内部类是非静态嵌套类型。所有嵌套类型都允许此行为,包括嵌套枚举和接口,它们必须是静态的,并且不能具有封闭实例。基本上,该模型简化为以下语句:嵌套代码具有对封闭代码的完全访问权限-反之亦然。
那么,为什么呢?我认为一个例子更好地说明了这一点。
想想你的身体和大脑。如果你往手臂里注射海洛因,你的大脑就会兴奋。如果你大脑的杏仁核区域看到他认为对你的人身安全构成威胁的东西,比如一只黄蜂,他会让你的身体转向另一个方向,然后逃之夭夭,而不需要你“考虑”两次。
所以,大脑是身体固有的一部分,奇怪的是,反过来也是如此。在这种密切相关的实体之间使用访问控制会丧失它们的关系声明。如果您确实需要访问控制,那么您需要将类更多地分离为真正不同的单元。在那之前,他们是同一个单位。进一步学习的一个驱动示例是查看JavaIterator通常是如何实现的。
Iterator
从封闭代码到嵌套代码的无限制访问使得向嵌套类型的字段和方法添加访问修饰符在很大程度上变得毫无用处。这样做会增加混乱,并且可能会给Java编程语言的新手提供一种错误的安全感。
内部类被视为外部类的属性。因此,无论内部类实例变量是否是私有的,外部类都可以像访问其其他私有属性(变量)一样毫无问题地进行访问。
class Outer{ private int a; class Inner{ private int b=0; } void outMethod(){ a = new Inner().b; } }