1. 程式人生 > >Java內部類是如何獲取外部類的private屬性

Java內部類是如何獲取外部類的private屬性

關於內部類是如何獲取外部類的private屬性

上一段程式碼:

public class OuterClass {
  private static String language = "en";
  private String region = "US";
  private String li;

  private void kk() {
    System.out.println("kk");
}

  public class InnerClass {
      public void printOuterClassPrivateFields() {
          String fields = "language="
+ language +region; System.out.println(fields); kk(); } } public static void main(String[] args) { OuterClass outer = new OuterClass(); OuterClass.InnerClass inner = outer.new InnerClass(); inner.printOuterClassPrivateFields(); } }

jdk1.8.0編譯
反編譯OuterClass:

public class OuterClass
{
  private static String language = "en";
  private String region = "US";
  private String li;

  private void kk()
  {
    System.out.println("kk");
  }

  public class InnerClass
  {
    public InnerClass() {}

    public void printOuterClassPrivateFields()
    {
      String fields = "language="
+ OuterClass.language + OuterClass.this.region; System.out.println(fields); OuterClass.this.kk(); } } public static void main(String[] args) { OuterClass outer = new OuterClass(); OuterClass tmp13_12 = outer; tmp13_12.getClass(); InnerClass inner = new InnerClass(tmp13_12); inner.printOuterClassPrivateFields(); } }

這是OuterClass$InnerClass的反編譯:

public class OuterClass$InnerClass
{
  public OuterClass$InnerClass(OuterClass paramOuterClass) {}

  public void printOuterClassPrivateFields()
  {
    String fields = "language=" + OuterClass.access$0() + OuterClass.access$1(**this.this$0**);
    System.out.println(fields);
    OuterClass.access$2(this.this$0);
  }
}

我們可以看到OuterClass裡面多了幾個形如access$X()的靜態方法
於是使用”javap -c “更深一步的反編譯:

Compiled from "OuterClass.java"
public class a.OuterClass {
  static {};
    Code:
       0: ldc           #12                 // String en
       2: putstatic     #14                 // Field language:Ljava/lang/String;
       5: return        

  public a.OuterClass();
    Code:
       0: aload_0       
       1: invokespecial #19                 // Method java/lang/Object."<init>":()V
       4: aload_0       
       5: ldc           #21                 // String US
       7: putfield      #23                 // Field region:Ljava/lang/String;
      10: return        

  public static void main(java.lang.String[]);
    Code:
       0: new           #1                  // class a/OuterClass
       3: dup           
       4: invokespecial #43                 // Method "<init>":()V
       7: astore_1      
       8: new           #44                 // class a/OuterClass$InnerClass
      11: dup           
      12: aload_1       
      13: dup           
      14: invokevirtual #46                 // Method java/lang/Object.getClass:()Ljava/lang/Class;
      17: pop           
      18: invokespecial #50                 // Method a/OuterClass$InnerClass."<init>":(La/OuterClass;)V
      21: astore_2      
      22: aload_2       
      23: invokevirtual #53                 // Method a/OuterClass$InnerClass.printOuterClassPrivateFields:()V
      26: return        

  static java.lang.String access$0();
    Code:
       0: getstatic     #14                 // **Field language:Ljava/lang/String;**
       3: areturn       

  static java.lang.String access$1(a.OuterClass);
    Code:
       0: aload_0       
       1: getfield      #23                 // **Field region:Ljava/lang/String;**
       4: areturn       

  static void access$2(a.OuterClass);
    Code:
       0: aload_0       
       1: invokespecial #66                 // **Method kk:()V**
       4: return        
}

這些程式碼都不好看,直接看註釋好了。
我們可以看到,編譯器幫我們生成了三個方法:access0(),access1(),access2()languageregion調kk()accessX的名字)

總結:
1、當內部類呼叫外部類的私有屬性(包括變數和方法)時,其真正的執行是呼叫了編譯器生成的屬性的靜態方法(即acess0,access1等)來獲取這些屬性值或呼叫方法。
(附:外部類訪問內部類的private屬性也是一樣的)
(再附:其中java官方文件 有這樣一句話

if the member or constructor is declared private, then access is
permitted if and only if it occurs within the body of the top level
class (§7.6) that encloses the declaration of the member or
constructor.
意思是 如果(內部類的)成員和構造方法設定成了私有修飾符,當且僅當其外部類訪問時是允許的。)

2、編譯器編譯的時候,在內部類中,會生成一個this$0的變數,這個變數應該就是外部類的例項 (附:為什麼非靜態內部類有可能造成記憶體洩漏:因為非靜態內部類會隱式持有外部類例項的引用,如果使用靜態內部類,將外部例項引用作為弱引用持有。)