JAVA匿名內部類的構造方法
阿新 • • 發佈:2018-12-26
與人討論匿名內部類的構造方法問題,自己寫程式碼看看原理到底是什麼樣子的。因為類是匿名的,所以就無從建立一個同名的構造方法了。但是可以直接呼叫父類的構造方法。測試程式碼如下:
Java程式碼
- package testtest;
- publicclass Main {
- publicstaticvoid main(String[] args) {
- InnerTest inner = new InnerTest();
- Test t = inner.get(3);
- System.out.println(t.getI());
- }
- }
- class Test {
- privateint i;
- public Test(int i) {
- this.i = i;
- }
- publicint
- return i;
- }
- }
- class InnerTest {
- public Test get(int x) {
- returnnew Test(x) {
- @Override
- publicint getI() {
- returnsuper.getI() * 10;
- }
- };
- }
- }
package testtest; public class Main { public static void main(String[] args) { InnerTest inner = new InnerTest(); Test t = inner.get(3); System.out.println(t.getI()); } } class Test { private int i; public Test(int i) { this.i = i; } public int getI() { return i; } } class InnerTest { public Test get(int x) { return new Test(x) { @Override public int getI() { return super.getI() * 10; } }; } }
編譯之後得到4個class檔案:Test.class,InnerTest.class,InnerTest$1.class以及Main.class。容易看出來,Main.class是測試類的class檔案,Test.class是超類Test的class檔案,InnerTest.class是InnerTest 的class檔案,最值得關注的就是匿名內部類的class檔案InnerTest$1.class。
首先javap -c InnerTest$1
Java程式碼- Compiled from "Main.java"
- class testtest.InnerTest$1extends
- final testtest.InnerTest this$0;
- testtest.InnerTest$1(testtest.InnerTest, int);
- Code:
- 0: aload_0
- 1: aload_1
- 2: putfield #1; //Field this$0:Ltesttest/InnerTest;
- 5: aload_0
- 6: iload_2
- 7: invokespecial #2; //Method testtest/Test."<init>〈init〉":(I)V
- 10: return
- publicint getI();
- Code:
- 0: aload_0
- 1: invokespecial #3; //Method testtest/Test.getI:()I
- 4: bipush 10
- 6: imul
- 7: ireturn
- }
- </init>
Compiled from "Main.java"
class testtest.InnerTest$1 extends testtest.Test{
final testtest.InnerTest this$0;
testtest.InnerTest$1(testtest.InnerTest, int);
Code:
0: aload_0
1: aload_1
2: putfield #1; //Field this$0:Ltesttest/InnerTest;
5: aload_0
6: iload_2
7: invokespecial #2; //Method testtest/Test."<init>〈init〉":(I)V
10: return
public int getI();
Code:
0: aload_0
1: invokespecial #3; //Method testtest/Test.getI:()I
4: bipush 10
6: imul
7: ireturn
}
</init>
很明顯,雖然我們看來是匿名內部類,但編譯的時候給這個類指定了名字
InnerTest$1,而且看出來是繼承自Test:
Java程式碼- class testtest.InnerTest$1extends testtest.Test
class testtest.InnerTest$1 extends testtest.Test
而且在這個類有構造方法:
Java程式碼- testtest.InnerTest$1(testtest.InnerTest, int);
testtest.InnerTest$1(testtest.InnerTest, int);
這裡也很容易理解,兩個引數,一個是匿名內部類的外部類引用直接傳了進來,這也是我們能在內部類中直接訪問外部類成員的實現原理。另外一個就是int型別的引數了。也就是說其實編譯器自動的給我們添加了帶引數的構造方法。繼續往下看:
7: invokespecial #2; //Method testtest/Test."<init>":(I)V
這就是呼叫父類的構造方法了 。
接下來 ,我們 只要看 InnerTest中 get方法 的 實現就可以了 :
- Compiled from "Main.java"
- class testtest.InnerTest extends java.lang.Object{
- testtest.InnerTest();
- Code:
- 0: aload_0
- 1: invokespecial #1; //Method java/lang/Object."<init>〈init〉":()V
- 4: return
- public testtest.Test get(int);
- Code:
- 0: new #2; //class testtest/InnerTest$1
- 3: dup
- 4: aload_0
- 5: iload_1
- 6: invokespecial #3; //Method testtest/InnerTest$1."<init>〈init〉":(Ltesttest/InnerTest;I)V
- 9: areturn
- }
- </init></init><PRE></PRE>
Compiled from "Main.java"
class testtest.InnerTest extends java.lang.Object{
testtest.InnerTest();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>〈init〉":()V
4: return
public testtest.Test get(int);
Code:
0: new #2; //class testtest/InnerTest$1
3: dup
4: aload_0
5: iload_1
6: invokespecial #3; //Method testtest/InnerTest$1."<init>〈init〉":(Ltesttest/InnerTest;I)V
9: areturn
}
</init></init>
到這裡一切都清楚了,InnerTest中對待匿名內部類和對待普通類一樣,
先是
- 0: new #2; //class testtest/InnerTest$1
0: new #2; //class testtest/InnerTest$1
然後呼叫其構造方法:
Java程式碼- 6: invokespecial #3; //Method testtest/InnerTest$1."〈init〉":(Ltesttest/InnerTest;I)V<PRE></PRE>
6: invokespecial #3; //Method testtest/InnerTest$1."〈init〉":(Ltesttest/InnerTest;I)V
<init></init><init>OK,一切都清楚了 。<br></init>