如何在内存级别通过类的成员函数访问类的私有成员?

时间:2016-04-27 08:14:55

标签: java memory-management jvm

class TestMemberOuter1{
 private int data=30;
 class Inner{
  void msg(){System.out.println("data is "+data);}
 }

 void display(){
  Inner in=new Inner();
  in.msg();
 }
 public static void main(String args[]){
  TestMemberOuter1 obj=new TestMemberOuter1();
  obj.display();
 }
}

为什么innerclass能够访问外部类的私有成员?

我想知道什么实现[在较低级别(可能在内存级别或特定于java实现或任何其他不确定)]能够在java中实现这种行为。

2 个答案:

答案 0 :(得分:3)

我认为你不需要任何内存级修改或逻辑实现来实现这一点,我认为java在内存级别会有任何巨大的代码逻辑来实现它。

记忆与它无关。 Private,public和protected只是一个访问过滤器,无论是私有,公共还是受保护,所有这些变量都将驻留在为对象分配的同一内存中。

私有,公共或受保护变量没有不同的内存分配。它们最终都是同一物体的属性。

然后编译器如何处理这个???

比这简单。

这些访问过滤器清楚地告知应允许他们访问的上下文。

私有:只有白色的类:)每当编译器看到提示被访问的变量时,任何在类的外边都会标记错误,这就是全部。

受保护:同一个包中的所有类:)每当编译器看到被保护的变量被访问时,任何out包都将标记错误,这就是全部。

公共:访问所有:)没有标志。

记得在上下文中访问变量导致编译器错误而不是运行时错误?出于同样的原因。

你不需要背后的巨大逻辑,只需保留私有,受保护和公共变量的列表,并检查它们的用法是否合适。

修改 根据您更新的问题"为什么内部类能够访问外部类的私有成员?"

从我上面解释的相同类比得出结论,允许在类中的任何地方访问私有变量。

现在你的内部类被宣布在哪里?作为你外在阶级的一部分本身并不是它。所以当你在内部类中访问外部类的私有变量时,编译器对它没有任何问题,因为你的内部类本身位于外部类中。

我希望我的回答有点意义:)快乐的编码。

答案 1 :(得分:0)

目前,内部类被编译为不同的类文件,但是当跨嵌套类访问private成员时,编译器将插入合成帮助器方法。合成方法本身将具有包私有访问权限,并在其自己的类中执行对private成员的访问。

这可以用private方法证明,因为它们的执行可以被跟踪,并将显示这些辅助方法的执行:

public class OuterClass {
    static class InnerClass {
        private static void test() {
            OuterClass.privateMethod();
        }
    }
    private static void privateMethod() {
        Thread.dumpStack();
    }
    public static void main(String[] args) {
        InnerClass.test();
    }
}

运行此程序将打印:

java.lang.Exception: Stack trace
    at java.lang.Thread.dumpStack(Thread.java:1329)
    at OuterClass.privateMethod(OuterClass.java:9)
    at OuterClass.access$000(OuterClass.java:2)
    at OuterClass$InnerClass.test(OuterClass.java:5)
    at OuterClass$InnerClass.access$100(OuterClass.java:3)
    at OuterClass.main(OuterClass.java:12)

这些嵌套类被编译为两个不同的类文件OuterClass.classOuterClass$InnerClass.class。您可以看到编译器已将合成方法access$100插入到OuterClass$InnerClass中,这允许main OuterClass方法调用private方法{{1}内部类。这个内部类方法又在外部类中调用了一个合成方法test,允许在access$000中调用privateMethod()

请注意,这种访问与使用Java 8的lambda表达式和方法引用执行的OuterClass成员访问不同。对于在该上下文中执行的成员访问,编译器不会生成任何辅助方法,并且有意未指定JVM使访问成为可能的方式,但我们可以说对于Oracle当前的JRE实现,将有一个运行时生成的类,确实能够绕过 private成员的访问限制,例如

private

import java.util.function.Consumer; public class OuterClass { static class InnerClass { static final Consumer<Runnable> TEST_METHOD=InnerClass::test; private static void test(Runnable outerMethod) { outerMethod.run(); } } private static void privateMethod() { Thread.dumpStack(); } public static void main(String[] args) { System.out.println(System.getProperty("java.version")); InnerClass.TEST_METHOD.accept(OuterClass::privateMethod); } } 开始,它会打印:

1.8.0_65

不显示此类辅助方法,但也过滤掉运行时生成的类。将java.lang.Exception: Stack trace at java.lang.Thread.dumpStack(Thread.java:1329) at OuterClass.privateMethod(OuterClass.java:12) at OuterClass$InnerClass.test(OuterClass.java:8) at OuterClass.main(OuterClass.java:16) 更改为

privateMethod()

揭示

private static void privateMethod() {
    for(StackTraceElement e:Thread.getAllStackTraces().get(Thread.currentThread()))
        System.out.println("\tat "+e);
}

生成的类具有访问 at java.lang.Thread.dumpThreads(Native Method) at java.lang.Thread.getAllStackTraces(Thread.java:1603) at OuterClass.privateMethod(OuterClass.java:12) at OuterClass$$Lambda$2/135721597.run(Unknown Source) at OuterClass$InnerClass.test(OuterClass.java:8) at OuterClass$InnerClass$$Lambda$1/471910020.accept(Unknown Source) at OuterClass.main(OuterClass.java:16) 成员的OuterClass$InnerClass$$Lambda$1/471910020OuterClass$$Lambda$2/135721597等奇特名称。请注意,这些类的生成是由有权访问这些private成员的类触发的,这些成员在允许创建此类函数对象之前已经过检查。