成員內部類
作為外部類的一個成員存在的類稱為成員內部類。
注意:
必須先創建外部類才能創建內部類。
成員內部類不能含有static的變量和方法。因為成員內部類需要先創建外部類,才能創建它自己的。
代碼:
public class Outer { private String s1 = "this is s1 in Outer"; private String s2 = "this is s2 in Outer"; public void method1() { // 外部類可通過內部類的對象調用內部類的私有成員變量或方法 System.out.println(new Inner().s1); System.out.println(new Inner().method2()); } private String method2() { return "this is method2 in Outer"; } public class Inner { private String s1 = "this is s1 in Inner"; public final static String s2 = "this is s2 in Inner"; public void method1() { // 內部類可直接使用外部類的私有成員變量或方法 System.out.println(s2); // 內部類和外部類有同名變量和方法時 System.out.println(s1); System.out.println(Outer.this.s1); System.out.println(method2()); System.out.println(Outer.this.method2()); } private String method2() { return "this is method2 in Inner"; } } }
調用:
public class MainClass { public static void main(String[] args) { Outer outer = new Outer(); System.out.println("------外部類測試--------"); outer.method1(); System.out.println("------內部類測試--------"); outer.new Inner().method1(); System.out.println(Outer.Inner.s2); } }
打印:
------外部類測試-------- this is s1 in Inner this is method2 in Inner ------內部類測試-------- this is s2 in Inner this is s1 in Inner this is s1 in Outer this is method2 in Inner this is method2 in Outer this is s2 in Inner
分析:
反編譯后自動生成文件:Outer$Inner.class
Outer 反編譯代碼1:
public class jichu.Outer { private java.lang.String s1; private java.lang.String s2; public jichu.Outer(); public void method1(); private java.lang.String method2(); static java.lang.String access$0(jichu.Outer); static java.lang.String access$1(jichu.Outer); static java.lang.String access$2(jichu.Outer); }
Outer 反編譯代碼2:
public class Outer { private String s1 = "this is s1 in Outer"; private String s2 = "this is s2 in Outer"; public void method1() { System.out.println(new Inner().s1); System.out.println(new Inner().method2()); } private String method2() { return "this is method2 in Outer"; } public class Inner { private String s1 = "this is s1 in Inner"; public static final String s2 = "this is s2 in Inner"; public Inner() {} public void method1() { System.out.println("this is s2 in Inner"); System.out.println(this.s1); System.out.println(Outer.this.s1); System.out.println(method2()); System.out.println(Outer.this.method2()); } private String method2() { return "this is method2 in Inner"; } } }
Outer$Inner反編譯代碼1:
public class jichu.Outer$Inner { private java.lang.String s1; public static final java.lang.String s2; final jichu.Outer this$0; public jichu.Outer$Inner(jichu.Outer); public void method1(); private java.lang.String method2(); static java.lang.String access$0(jichu.Outer$Inner); static java.lang.String access$1(jichu.Outer$Inner); }
Outer$Inner反編譯代碼2:
public class Outer$Inner { private String s1 = "this is s1 in Inner"; public static final String s2 = "this is s2 in Inner"; public Outer$Inner(Outer paramOuter) {} public void method1() { System.out.println("this is s2 in Inner"); System.out.println(this.s1); System.out.println(Outer.access$0(this.this$0)); System.out.println(method2()); System.out.println(Outer.access$1(this.this$0)); } private String method2() { return "this is method2 in Inner"; } }
MainClass反編譯代碼:
public class MainClass { public static void main(String[] args) { Outer outer = new Outer(); System.out.println("------外部類測試--------"); outer.method1(); System.out.println("------內部類測試--------");
Outer tmp33_32 = outer;
tmp33_32.getClass();
new Outer.Inner(tmp33_32).method1();
System.out.println("this is s2 in Inner"); } }
MainClass中有代碼:
Outer tmp33_32 = outer; new Outer.Inner(tmp33_32).method1();
可以看出生成Inner對象時,將tmp33_32(即外部類對象outer的引用)傳入Inner的構造器中,這個構造器從哪來的?
Outer$Inner中有代碼:
public jichu.Outer$Inner(jichu.Outer);
這是編譯器自動生成的構造器,參數為外部類對象,通過將tmp33_32傳入構造器,來建立外部類與內部類的聯系。
Outer$Inner中有代碼:
final jichu.Outer this$0;
猜測Outer$Inner通過構造器將外部類對象的引用賦予變量this$0;
當內部類調用外部類的私有變量和方法時,
Outer$Inner中有代碼:
System.out.println(Outer.access$0(this.this$0)); System.out.println(Outer.access$1(this.this$0)); System.out.println(Outer.access$2(this.this$0));
Outer中有代碼:
static java.lang.String access$0(jichu.Outer); static java.lang.String access$1(jichu.Outer); static java.lang.String access$2(jichu.Outer);
access$i是JAVA編譯器自動生成的十分重要的方法(該方法的個數與內部類要訪問的外部類的變量個數相關),目的是:用於內部類訪問外部類的數據成員時使用。
在內部類中通過Outer.access$i(this.this$0)的方式,可實現對外部類的私有變量和方法的隨意訪問。
疑問
成員內部類不能含有static的變量和方法。但是測試發現變量可以被static final修飾,為什么?
主要是因為final類型在編譯期間jvm有優化,常量池會維護這些變量。雖然非靜態內部類不能脫離外部類這個上下文實例化,但是常量池使得final變量脫離了類實例化這個條件,編譯期間便可確定。
Outer$Inner中有代碼:
public static final String s2 = "this is s2 in Inner";
MainClass中有代碼:
System.out.println(Outer.Inner.s2);
通過Outer.Inner.s2來訪問static final的變量;
MainClass反編譯代碼中有:
System.out.println("this is s2 in Inner");
s2在編譯時被"this is s2 in Inner"直接替換。
總結:
1、內部類可直接使用外部類的私有成員變量或方法,外部類對內部類是可見的;
2、外部類中可以創建私有內部類對象。
3、外部類可通過內部類的對象調用內部類的私有成員變量或方法;
4、當內部類與外部類有同名成員變量和方法時,內部類可通過Outer.this方式來引用外部類成員。
5、必須先創建外部類才能創建內部類。
6、成員內部類不能含有static的變量和方法。因為成員內部類需要先創建外部類,才能創建它自己的。