標籤:inner 結果 代碼 outer stat new 參數 main 私人屬性
情境一:匿名內部類、非靜態內部類隱式持有外部類的引用非靜態內部類樣本
1 public class Test { 2 private int testValue = 1; 3 4 public class Outer { 5 private int outerValue = 2; 6 7 public class Inner { 8 private int innerValue = 3; 9 10 public void print() {11 System.out.println("testValue=" + testValue + ", outerValue=" + outerValue12 + ", innerValue=" + innerValue);13 }14 }15 }16 17 public static void main(String[] args) {18 Test test = new Test();19 Outer outer = test.new Outer();20 Outer.Inner inner = outer.new Inner();21 inner.print();22 }23 }
如19-20行所示,在外部類外執行個體化非靜態內部類時,需要通過外部類的執行個體.new進行構造。
使用 javac Test.java進行編譯,產生Test$Outer$Inner.class、Test$Outer.class、Test.class幾個class檔案。使用javap -p class_file_name查看,結果如下:
? ~/Programming/java javap -p "Test.class"Compiled from "Test.java"public class Test { private int testValue; public Test(); public static void main(java.lang.String[]); static int access$000(Test);}? ~/Programming/java javap -p "Test\$Outer.class"Compiled from "Test.java"public class Test$Outer { private int outerValue; final Test this$0; public Test$Outer(Test); static int access$100(Test$Outer);}? ~/Programming/java javap -p "Test\$Outer\$Inner.class"Compiled from "Test.java"public class Test$Outer$Inner { private int innerValue; final Test$Outer this$1; public Test$Outer$Inner(Test$Outer); public void print();}
從產生的class檔案可以看出,內部類的建構函式被自動加上以外部類對象為參數,並且內部類有一個成員指向一個外部類對象(Test$Outer的this$0,Test$Outer$Inner的this$1)。
外部類和內部類之間可以相互訪問對方的私人屬性,而編譯之後每個類都是單獨的class檔案,因此編譯器會自動產生相應的包存取權限的靜態方法(只有在需要的時候才會產生),來支援這種私人屬性的訪問(Test中為testValue產生的access$000靜態方法,Test$Outer中為outerValue產生的access$100靜態方法)。
匿名內部類樣本
1 public class AnonymousTest { 2 private int testValue = 1; 3 4 public interface IAnonymous { 5 void print(); 6 } 7 8 private final IAnonymous anonymous = new IAnonymous() { 9 private int innerValue = 2;10 11 public void print() {12 System.out.println("testValue=" + testValue + ", innerValue=" + innerValue);13 }14 };15 16 public static void main(String[] args) {17 AnonymousTest test = new AnonymousTest();18 test.anonymous.print();19 }20 }
使用 javac AnonymousTest.java進行編譯,產生AnonymousTest$1.class、AnonymousTest$IAnonymous.class、AnonymousTest.class幾個class檔案。使用javap -p class_file_name查看,結果如下:
? ~/Programming/java javap -p AnonymousTest.classCompiled from "AnonymousTest.java"public class AnonymousTest { private int testValue; private final AnonymousTest$IAnonymous anonymous; public AnonymousTest(); public static void main(java.lang.String[]); static int access$000(AnonymousTest);}? ~/Programming/java javap -p "AnonymousTest\$IAnonymous.class"Compiled from "AnonymousTest.java"public interface AnonymousTest$IAnonymous { public abstract void print();}? ~/Programming/java javap -p "AnonymousTest\$1.class"Compiled from "AnonymousTest.java"class AnonymousTest$1 implements AnonymousTest$IAnonymous { private int innerValue; final AnonymousTest this$0; AnonymousTest$1(AnonymousTest); public void print();}
從產生的class檔案可以看出,匿名內部類與非靜態內部類一樣,同樣持有外部類的引用。不同之處在於,匿名內部類的名字是外部類$數字。匿名內部類可以定義成員變數和成員函數,但是只能在本類中訪問,也無法通過JAVA代碼直接構造匿名內部類的新的執行個體。
結論
由於非靜態內部類和匿名內部類的執行個體會隱式持有外部類執行個體的引用,因此,若其生命週期比外部類執行個體長,會導致外部類執行個體無法回收而產生泄漏。因此,在使用要格外注意。
Android記憶體流失的情境 (1)