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

时间:2009-11-26 05:34:05

标签: 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 this allowed??
    }
}

为什么允许这种行为?

10 个答案:

答案 0 :(得分:81)

内部类只是一种干净地分离一些真正属于原始外部类的功能的方法。它们旨在满足您的2个要求时使用:

  1. 如果外部类中的某些功能是在单独的类中实现的,那么它将是最清晰的。
  2. 即使它在一个单独的类中,功能也与外部类的工作方式密切相关。
  3. 鉴于这些要求,内部类可以完全访问其外部类。因为它们基本上是外部类的成员,所以它们可以访问外部类的方法和属性 - 包括私有类。

答案 1 :(得分:57)

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

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

答案 2 :(得分:50)

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

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

答案 3 :(得分:16)

在与此类似的另一个问题上出现了正确的答案: Why can the private member of an nested class be accessed by the methods of the enclosing class?

它说JLS - Determining Accessibility上有私人作用域的定义:

  

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

答案 4 :(得分:5)

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

abyx相矛盾,声明类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);
    }
}

答案 5 :(得分:3)

访问限制是按类进行的。在类中声明的方法无法访问所有实例/类成员。理所当然,内部类也可以不受限制地访问外部类的成员,外部类可以自由地访问内部类的成员。

通过将一个类放在另一个类中,你将它与实现紧密联系起来,任何属于实现的东西都应该可以访问其他部分。

答案 6 :(得分:3)

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

在你的情况下,如果类能够看到彼此的内部工作是没有意义的 - 这基本上意味着内部类可以简单地成为常规类,你可以将内部类声明为{ {1}}。使用static class XYZ意味着他们不会共享状态(例如,static将无效,您将需要使用new ABC().new XYZ()。 但是,如果是这种情况,你应该考虑new ABC.XYZ()是否真的应该是一个内部类,也许它应该得到它自己的文件。有时创建静态内部类是有意义的(例如,如果您需要一个实现外部类正在使用的接口的小类,并且在其他任何地方都没有用)。但是大约有一半的时间应该成为一个外部阶层。

答案 7 :(得分:1)

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

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

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

想想你的身体和大脑。如果你将海洛因注入你的手臂,你的大脑就会变高。如果你的大脑杏仁核区域看到他认为对你个人安全构成威胁,比如说是一个黄蜂,那么他会让你的身体反过来转向山丘,而不是你“思考”两次。

因此,大脑是身体的内在组成部分 - 奇怪的是,另一种方式也是如此。在这些密切相关的实体之间使用访问控制会丧失他们对关系的主张。如果确实需要访问控制,那么您需要将类更多地分成真正不同的单元。在那之前,他们是同一个单位。进一步研究的一个驱动示例是查看Java Iterator通常是如何实现的。

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

答案 8 :(得分:-1)

内部类被视为外部类的属性。因此,无论Inner类实例变量是否为私有,外部类都可以访问而没有任何问题,就像访问其他私有属性(变量)一样。

class Outer{

private int a;

class Inner{
private int b=0;
}

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

答案 9 :(得分:-2)

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