1. 程式人生 > >JAVA匿名內部類的構造方法

JAVA匿名內部類的構造方法

與人討論匿名內部類的構造方法問題,自己寫程式碼看看原理到底是什麼樣子的。因為類是匿名的,所以就無從建立一個同名的構造方法了。但是可以直接呼叫父類的構造方法。測試程式碼如下: Java程式碼 複製程式碼 收藏程式碼
  1. package testtest;   
  2. publicclass Main {   
  3. publicstaticvoid main(String[] args) {   
  4.         InnerTest inner = new InnerTest();   
  5.         Test t = inner.get(3);   
  6.         System.out.println(t.getI());   
  7.     }   
  8. }   
  9. class Test {   
  10. privateint i;   
  11. public Test(int i) {   
  12. this.i = i;   
  13.     }   
  14. publicint
     getI() {   
  15. return i;   
  16.     }   
  17. }   
  18. class InnerTest {   
  19. public Test get(int x) {   
  20. returnnew Test(x) {   
  21. @Override
  22. publicint getI() {   
  23. returnsuper.getI() * 10;   
  24.             }   
  25.         };   
  26.     }   
  27. }  
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程式碼 複製程式碼 收藏程式碼
  1. Compiled from "Main.java"
  2. class testtest.InnerTest$1extends
     testtest.Test{   
  3. final testtest.InnerTest this$0;   
  4. testtest.InnerTest$1(testtest.InnerTest, int);   
  5.   Code:   
  6. 0:   aload_0   
  7. 1:   aload_1   
  8. 2:   putfield    #1//Field this$0:Ltesttest/InnerTest;
  9. 5:   aload_0   
  10. 6:   iload_2   
  11. 7:   invokespecial   #2//Method testtest/Test."<init>〈init〉":(I)V
  12. 10:  return
  13. publicint getI();   
  14.   Code:   
  15. 0:   aload_0   
  16. 1:   invokespecial   #3//Method testtest/Test.getI:()I
  17. 4:   bipush  10
  18. 6:   imul   
  19. 7:   ireturn   
  20. }   
  21. </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程式碼 複製程式碼 收藏程式碼
  1. class testtest.InnerTest$1extends testtest.Test  
class testtest.InnerTest$1 extends testtest.Test

而且在這個類有構造方法: 

Java程式碼 複製程式碼 收藏程式碼
  1. testtest.InnerTest$1(testtest.InnerTest, int);  
testtest.InnerTest$1(testtest.InnerTest, int);

這裡也很容易理解,兩個引數,一個是匿名內部類的外部類引用直接傳了進來,這也是我們能在內部類中直接訪問外部類成員的實現原理。另外一個就是int型別的引數了。也就是說其實編譯器自動的給我們添加了帶引數的構造方法。繼續往下看: 
7: invokespecial #2; //Method testtest/Test."<init>":(I)V
這就是呼叫父類的構造方法了 。
接下來 ,我們 只要看 InnerTest中 get方法 的 實現就可以了 :

Csharp程式碼 複製程式碼 收藏程式碼
  1. Compiled from "Main.java"
  2. class testtest.InnerTest extends java.lang.Object{   
  3. testtest.InnerTest();   
  4.   Code:   
  5.    0:   aload_0   
  6.    1:   invokespecial   #1; //Method java/lang/Object."<init>〈init〉":()V
  7.    4:   return
  8. public testtest.Test get(int);   
  9.   Code:   
  10.    0:   new #2; //class testtest/InnerTest$1
  11.    3:   dup   
  12.    4:   aload_0   
  13.    5:   iload_1   
  14.    6:   invokespecial   #3; //Method testtest/InnerTest$1."<init>〈init〉":(Ltesttest/InnerTest;I)V
  15.    9:   areturn   
  16. }   
  17. </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中對待匿名內部類和對待普通類一樣,

先是

Csharp程式碼 複製程式碼 收藏程式碼
  1. 0:  new #2; //class testtest/InnerTest$1
0:	new	#2; //class testtest/InnerTest$1

然後呼叫其構造方法:

Java程式碼 複製程式碼 收藏程式碼
  1. 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>