内部类访问封闭类别'更高版本的Java的私人成员?

发布于 2025-01-28 02:59:18 字数 3732 浏览 4 评论 0原文

我一直在尝试了解Java嵌套类的机制。

考虑以下代码:

public class OuterClass {

private String privateOuterField = "Private Outer Field";

public static String publicStaticOuterField = "Public static outer field";

private static String privateStaticOuterField = "private static outer field";


class InnerClass{
    private String privateInnerField = "Private Inner Field";
    
    //non-final static data members not allowed in java 1.8 but allowed in java 17.0
    //private static String innerClassStaticField = "Private Inner Class Static Field";   
    
    public void accessMembers() {
        System.out.println(privateOuterField);
        System.out.println(publicStaticOuterField);
    }
}

static class StaticInnerClass{
    
    private String privateStaticInnerField = "Private Inner Field of static class";
    
    public void accessMembers(OuterClass outer) {
        //System.out.println(privateOuterField);  //error
        System.out.println(outer.privateOuterField);
        System.out.println(publicStaticOuterField);
        System.out.println(privateStaticOuterField);
    }
}
    
public static void main(String[] args) {
    
    OuterClass outerObj = new OuterClass();
    OuterClass.InnerClass innerObj = outerObj.new InnerClass();
    
    StaticInnerClass staticInnerObj = new StaticInnerClass();
    
    innerObj.accessMembers();
    staticInnerObj.accessMembers(outerObj);
    

}

}

我知道内部类是编译器的现象,虚拟机不了解它们。内部类被翻译成带有$划分的外部和内类名称的常规类文件。

为了了解更多详细信息,我尝试使用Javap -p命令在Java 1.8中汇编的类文件。

我得到以下结果: OuterClass:

public class staticNestedClasses.OuterClass {
  private java.lang.String privateOuterField;
  public static java.lang.String publicStaticOuterField;
  private static java.lang.String privateStaticOuterField;
  public staticNestedClasses.OuterClass();
  public static void main(java.lang.String[]);
  static java.lang.String access$000(staticNestedClasses.OuterClass);
  static java.lang.String access$100();
  static {};
}

InnerClass:

class staticNestedClasses.OuterClass$InnerClass {
  private java.lang.String privateInnerField;
  final staticNestedClasses.OuterClass this$0;
  staticNestedClasses.OuterClass$InnerClass(staticNestedClasses.OuterClass);
  public void accessMembers();
}

在这里我们可以看到编译器通过构造函数将外部类的引用传递给内类,以便它可以评估外部类的字段和方法:

staticNestedClasses.OuterClass$InnerClass(staticNestedClasses.OuterClass);

此外部类参考存储在最终的staticnestedClasses.outerclass.OuterClass.OuterClass.OuterClass中。 /code>

utoutclass $ innerClass类无法通过外部类参考直接访问私有成员,因此,每当编译器从内类中检测到对私人成员的访问时,它就会在外类中生成访问者方法(或Getter方法)。

在外部类的分解文件中,我们可以看到编译器生成了这些访问者方法。

static java.lang.String access$000(staticNestedClasses.OuterClass);
static java.lang.String access$100();

但是,当我在Java 17.0中编译了相同的代码并拆卸类文件时,我会得到以下结果。

外类:

public class staticNestedClasses.OuterClass {
  private java.lang.String privateOuterField;
  public static java.lang.String publicStaticOuterField;
  private static java.lang.String privateStaticOuterField;
  public staticNestedClasses.OuterClass();
  public static void main(java.lang.String[]);
  static {};
}

OuterClass $ InnerClass:

class staticNestedClasses.OuterClass$InnerClass {
  private java.lang.String privateInnerField;
  private static java.lang.String innerClassStaticField;
  final staticNestedClasses.OuterClass this$0;
  staticNestedClasses.OuterClass$InnerClass(staticNestedClasses.OuterClass);
  public void accessMembers();
  static {};
}

此处编译器没有生成任何访问者方法,但代码工作正常。

那么内部班级如何访问外部班级的私人成员?

I have been trying to understand the mechanism of nested classes in java.

Consider the following code:

public class OuterClass {

private String privateOuterField = "Private Outer Field";

public static String publicStaticOuterField = "Public static outer field";

private static String privateStaticOuterField = "private static outer field";


class InnerClass{
    private String privateInnerField = "Private Inner Field";
    
    //non-final static data members not allowed in java 1.8 but allowed in java 17.0
    //private static String innerClassStaticField = "Private Inner Class Static Field";   
    
    public void accessMembers() {
        System.out.println(privateOuterField);
        System.out.println(publicStaticOuterField);
    }
}

static class StaticInnerClass{
    
    private String privateStaticInnerField = "Private Inner Field of static class";
    
    public void accessMembers(OuterClass outer) {
        //System.out.println(privateOuterField);  //error
        System.out.println(outer.privateOuterField);
        System.out.println(publicStaticOuterField);
        System.out.println(privateStaticOuterField);
    }
}
    
public static void main(String[] args) {
    
    OuterClass outerObj = new OuterClass();
    OuterClass.InnerClass innerObj = outerObj.new InnerClass();
    
    StaticInnerClass staticInnerObj = new StaticInnerClass();
    
    innerObj.accessMembers();
    staticInnerObj.accessMembers(outerObj);
    

}

}

I know that inner classes are a phenomenon of the compiler, virtual machine are not aware of them. Inner classes are translated into regular class files with $ delimiting outer and inner class name.

To understand this mechanism in more details, I tried to disassemble the class file compiled in java version 1.8 using javap -p command.

I got the following results:
OuterClass:

public class staticNestedClasses.OuterClass {
  private java.lang.String privateOuterField;
  public static java.lang.String publicStaticOuterField;
  private static java.lang.String privateStaticOuterField;
  public staticNestedClasses.OuterClass();
  public static void main(java.lang.String[]);
  static java.lang.String access$000(staticNestedClasses.OuterClass);
  static java.lang.String access$100();
  static {};
}

InnerClass:

class staticNestedClasses.OuterClass$InnerClass {
  private java.lang.String privateInnerField;
  final staticNestedClasses.OuterClass this$0;
  staticNestedClasses.OuterClass$InnerClass(staticNestedClasses.OuterClass);
  public void accessMembers();
}

Here we can see that the compiler passes the reference of outer class to inner class through constructor so that it can assess outer class's fields and methods:

staticNestedClasses.OuterClass$InnerClass(staticNestedClasses.OuterClass);

this outer class reference is stored in final staticNestedClasses.OuterClass this$0

But OuterClass$InnerClass class cannot directly access private members through outer class reference, so whenever the compiler detects access to private members from inner class, it generates accessor method (or getter methods) in outer class.

In the disassembled file of outer class we can see that the compiler generated these accessor methods.

static java.lang.String access$000(staticNestedClasses.OuterClass);
static java.lang.String access$100();

But when I compiled the same code in java 17.0 and disassembled the class file I got the following result.

Outer class:

public class staticNestedClasses.OuterClass {
  private java.lang.String privateOuterField;
  public static java.lang.String publicStaticOuterField;
  private static java.lang.String privateStaticOuterField;
  public staticNestedClasses.OuterClass();
  public static void main(java.lang.String[]);
  static {};
}

OuterClass$InnerClass:

class staticNestedClasses.OuterClass$InnerClass {
  private java.lang.String privateInnerField;
  private static java.lang.String innerClassStaticField;
  final staticNestedClasses.OuterClass this$0;
  staticNestedClasses.OuterClass$InnerClass(staticNestedClasses.OuterClass);
  public void accessMembers();
  static {};
}

Here compiler did not generate any accessor methods but the code worked fine.

So how did the inner class access private members of outer class?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

云淡月浅 2025-02-04 02:59:18

唯一阻止类访问其他类的private成员的东西是拒绝访问权限的JVM(或恰恰是其验证者)。因此,它需要使它成为可能的一切,就是JVM的合作。

尽管Java  1.1以不需要更改JVM的方式引入了内部类,但JVM经历了许多更改,同时,直到Java  11进行更改。

Java  11介绍了 nesthost nestmembers 字节属性属性允许类文件表示它们属于所谓的“ nest”。允许所有属于同一巢穴的类访问私有成员。如前所述,唯一需要更改的是JVM的验证者,可以允许使用此类访问。当然,还可以使用此功能的编译器。另请参见 jep  181

因此,您可以说JVM仍然对内部类别一无所知,因为哪个类属于巢穴,都是由生成类文件的任何工具决定的(例如Java源代码编译器)。因此,可以使用Nests与其他工具一起生产类文件,而无需遵循内部类语义。

为了完成,应该提到类文件还包含有关内部类关系的信息,使用 innerClasses 属性。但这仅由编译器和反思使用,而JVM在决定访问是否合法时不会使用此信息。

The only thing that stops a class from accessing another class’s private members, is the JVM (or precisely its verifier) rejecting the access. So all it needs to make it possible, is the collaboration of the JVM to allow it.

While Java 1.1 introduced inner classes in a way that did not require changes to the JVM, the JVM has gone through so many changes in the meanwhile, that it is rather surprising that it took until Java 11 to change that.

Java 11 introduced the NestHost and NestMembers bytecode attributes to allow class files to denote that they belong to a so called “nest”. All classes belonging to the same nest are allowed to access each others private members. As said, the only thing that needed to be changed, is the JVM’s verifier to allow such access. And, of course, the compiler to utilize this feature. See also JEP 181.

So you could say that the JVM still doesn’t know anything about inner classes, because which classes belong to a nest, is decided by whichever tool generated the class files (e.g. the Java source code compiler). So it is possible to produce class files with other tools using nests without following the inner class semantic.

For completion, it should be mentioned that class files do also contain the information about inner class relationships, using the InnerClasses attribute. But this is only used by compilers and Reflection, whereas the JVM doesn’t use this information when deciding whether an access is legal or not.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文