当前位置: 首页 > 知识库问答 >
问题:

内部类如何访问封闭类的私有成员在更高版本的Java?

鞠建安
2023-03-14

我一直在试图理解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 {};
}

内部类:

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

这个外部类引用存储在最终的StaticNestedClass中。OuterClass此$0

但是OuterClass$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 {};
}

这里编译器没有生成任何访问器方法,但代码运行良好。

那么,内部类是如何访问外部类的私有成员的呢?

共有1个答案

端木兴国
2023-03-14

阻止一个类访问另一个类的私有成员的唯一原因是JVM(或者确切地说是它的验证器)拒绝了访问。因此,它只需要JVM的协作就可以实现。

而Java

Java

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

最后,应该提到的是,类文件还包含关于内部类关系的信息,使用了innerclases属性。但这仅由编译器和反射使用,而JVM在决定访问是否合法时不使用这些信息。

 类似资料:
  • 我认为内部类的成员,即使被声明为私有的,也可以从它的封闭类中访问。但是我遇到了以下代码结构的编译时错误。我的顶级类实现了Iterable接口。Iterator在内部类中实现。当内部类的一个实例通过iterator()方法获得时,我无法使用该实例访问数据栏。 有什么建议吗?

  • 问题内容: 在Java中,内部类可以访问封闭类的私有成员。但是外层可以访问内层的私有成员吗?这与内部类是否静态无关。我认为这是不正确的,但是以下代码似乎可以编译并正常工作。 问题答案: 是的,没关系。从JLS,第6.6.1节中: 否则,如果声明了成员或构造函数,则仅当访问发生在封装成员或构造函数的声明的顶级类(第7.6节)的主体内时,才允许访问。 您甚至可以在另一个嵌套类型Y中引用嵌套类型X的私有

  • 我正在装一个应用程序,它工作得很好。但当我使用 在OuterClass中,并通过

  • 问题内容: 我观察到外部类可以访问内部类的私有实例变量。这怎么可能?这是演示相同代码的示例代码: 为什么允许这种行为? 问题答案: 内部类只是一种将真正属于原始外部类的功能完全分开的方法。当你有两个要求时,可以使用它们: 如果是在单独的类中实现的,那么外部类中的某些功能将最为清晰。 即使在单独的类中,该功能也与外部类的工作方式紧密相关。 鉴于这些要求,内部类可以完全访问其外部类。由于它们基本上是外

  • 问题内容: 谁能告诉我私人会员的访问级别?我一直对这段代码感到困惑:为什么可以在外部类的“打印”方法中访问Line类的私有成员k? 问题答案: 规则在有关可访问性的JLS章中 否则,如果声明了成员或构造函数,则仅当访问发生 在封装成员或构造函数的声明的顶级类(第7.6节)的主体内 时,才允许访问 。 在这里,成员字段在类中声明。当您在方法中访问它时,您正在 包含该 成员 的声明的顶级类的主体中对其

  • 我有一个Java类(),它使用内部构建器类(),作为构建的一部分访问私有变量,如下所示: 使用方法如下: 我试图利用Android Studio将Java代码迁移到Kotlin的能力,这给了我以下结果: 但是,编译失败,出现以下情况: 因为(如静态编程语言留档所述): 在Kotlin中,外部类看不到内部类的私有成员。 此代码在块中运行,其中: 我知道我可以对其进行重构,以避免尝试这种访问(更改外部