2015-11-15 85 views
1

我很熟悉所有的局部變量將被存儲在棧內存中,並且對象和靜態變量將被存儲在堆中。但是當我遇到以下使我困惑的代碼時。方法中的類如何訪問該方法的局部變量?

public class Outer { 

    private int a =10; 

    public void outerMethod() {  
     int x = 30; 

     class Inner { 
      private int b = 20; 
      public void innerMethod() { 
      System.out.println("The Value of a is: "+a); 
      System.out.println("The Value of b is: "+b); 
      System.out.println("The Value of x is: "+x); 
      } 
     }; 

     Inner inner = new Inner(); 
     inner.innerMethod(); 
    } 
} 

上述代碼運行良好。但我這裏的問題是x是outerMethod()的局部變量。當我們創建一個Outer類的對象並調用outerMethod()時,x將被存儲在一個棧框架中,並且我還定義了一個Inner類的類定義並創建了它的對象, m調用它的innerMethod()。
這樣內部類的對象必須存儲在堆內部。如果是這樣的話,它怎麼可以訪問x ??

+0

你爲什麼要這麼做?僅僅因爲它的合法性並不意味着它有用。 – QuakeCore

+1

我只是想知道原因。不管它是否有用,都會在我身邊 –

+0

@KishoreKumarKorada「無論它是否有用」,''Inner'類在關閉'x'的方式是一個非常有用的特性,稱爲'closure',它在事件處理中被頻繁使用 – Ramanlfc

回答

3

Inner只能訪問x如果x是final(或來自Java 8 effectively final)。在封面下,編譯器會注意到Inner對象使用哪些外部變量,並將它們傳遞給Inner構造函數(這些將是由編譯器生成的合成構造函數參數,所以您不會看到它們)。然後它們將成爲Inner類的最終(再次,合成=生成並隱藏)類。當您在Inner類代碼中引用x時,編譯器會將其替換爲Inner類中複合值的合成x字段的引用。

你可以看到更多的細節有了這個有用的命令:

javac -d . -XD-printflat MyFile.java 

將提出符合實際的字節代碼的Java代碼:

class Outer { 

    /*synthetic*/ static int access$000(Outer x0) { 
     return x0.a; 
    } 

    Outer() { 
     super(); 
    } 
    private int a = 10; 

    public void outerMethod() { 
     int x = 30; 
     /*synthetic*/ { 
     } 
     ; 
     Outer$1Inner inner = new Outer$1Inner(this, x); 
     inner.innerMethod(); 
    } 
} 

class Outer$1Inner { 
    /*synthetic*/ final Outer this$0; 
    /*synthetic*/ final int val$x; 

    Outer$1Inner(final Outer this$0, /*synthetic*/ final int val$x) { 
     // Notice the synthetic references to the surrounding Outer object 
     // and to the x local variable 
     // Both become fields of Inner 

     this.this$0 = this$0; 
     this.val$x = val$x; 
     super(); 
    } 
    private int b = 20; 

    public void innerMethod() { 
     System.out.println("The Value of a is: " + Outer.access$000(this$0)); 
     System.out.println("The Value of b is: " + b); 
     System.out.println("The Value of x is: " + val$x); 
    } 
} 

你也可以看到如何Outer.a訪問 - 因爲Inner被編譯爲「普通舊Java類」時,它必須尊重可見性修飾符,因此JVM將不允許直接訪問專用字段Outer.a。但是,在編譯期間,編譯器會注意到您要從Inner訪問此字段,並且由於它是內部類,將生成訪問器方法Outer.access$000()。由於InnerOuter對象有參考,因此它可以調用Outer.access$000(referenceToOuter)並獲得值Outer.a

+0

哇。非常感謝你的好解釋。現在對我來說很有意義 –

0

Inner class object has a link to your Outer class object。 Inner正在做什麼基本上是捕獲或關閉您的本地變量x,從而增加它的「終生」,因此您的Inner類可以訪問x

1

的確,Inner類將被編譯爲相同包中的單獨類。你是正確的,通常你不應該訪問私人方法和Outer類的字段。

但是,編譯器將在字節碼級別創建一些合成包私有方法來實現此功能。現在,您的Inner類可以訪問這些自動生成(隱藏)package-private(不含訪問修飾符)的方法。

相關問題