天天看點

黑馬程式員 匿名内部類連名字都沒有,會有構造函數麼?

---------------------- <a href="http://edu.csdn.net" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="blank">ASP.Net+Android+IO開發S</a>、<a href="http://edu.csdn.net" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="blank">.Net教育訓練</a>、期待與您交流! ----------------------

我在上一篇部落格中說到匿名内部類,裡面提到一句話,說匿名内部類是沒有構造函數的,但是卻可以執行個體化一個對象,這是百度的,我第一次看到這句話時楞了一下,為什麼呢?沒有構造函數,拿什麼區建立執行個體呢?

查了查,原來是因為匿名内部類連名字的都沒有,怎麼會有構造函數呢,總不能寫一對括号吧,後來想想内部類其實類似父子類,既然繼承父類,肯定得調用父類的構造函數啦,于是我就在網上查了一篇部落格,位址是http://zangxt.iteye.com/blog/421560,裡面就舉例說明了匿名内部類調用父類的構造函數,内容我就直接拿過來了:

Java代碼  

黑馬程式員 匿名内部類連名字都沒有,會有構造函數麼?
  1. package testtest;  
  2. public class Main {  
  3.     public static void 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.     private int i;  
  11.     public Test(int i) {  
  12.         this.i = i;  
  13.     }  
  14.     public int getI() {  
  15.         return i;  
  16.     }  
  17. }  
  18. class InnerTest {  
  19.     public Test get(int x) {  
  20.         return new Test(x) {  
  21.             @Override  
  22.             public int getI() {  
  23.                 return super.getI() * 10;  
  24.             }  
  25.         };  
  26.     }  
  27. }  

編譯之後得到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$1 extends 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. public int 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>  

很明顯,雖然我們看來是匿名内部類,但編譯的時候給這個類指定了名字InnerTest$1,而且看出來是繼承自Test:

  1. class testtest.InnerTest$1 extends testtest.Test  

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

  1. 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>  

到這裡一切都清楚了,InnerTest中對待匿名内部類和對待普通類一樣,先是

  1. 0:  new #2; //class testtest/InnerTest$1  

然後調用其構造方法:

  1. 6: invokespecial #3; //Method testtest/InnerTest$1."〈init〉":(Ltesttest/InnerTest;I)V<pre></pre>  

但如果匿名内部類是實作的一個接口呢?接口也是沒有構造函數的,為什麼呢?因為接口沒有具體的實作啊,

如果有構造函數就不算接口了,而是一個抽象函數,so,這下子父類也沒有構造函數了,匿名内部類調用誰去?

大神就是多,她告訴我繼承和實作是不一樣的,如果普通類沒指定父類隻實作了接口,那這個類就預設是Object的子類。

好吧,再舉個實作接口的例子:

  1. public class Main { 
  2. public static void main(String[] args) { 
  3. InnerTest inner = new InnerTest(); 
  4. Test t = inner.get(); 
  5. System.out.println(t.getI()); 
  6. interface Test { 
  7. public int getI();
  8. class InnerTest { 
  9. public Test get() { 
  10. return new Test() { 
  11. // @Override 
  12. public int getI() { 
  13. return 10; 
  14. }; 
  15. javap -c InnerTest$$$$$$$$1

複制代碼 運作javap -c InnerTest$1的結果

  1. class InnerTest$$$$1 implements Test {
  2. final InnerTest this$$$$0;
  3. InnerTest$$$$1(InnerTest);
  4. Code:
  5. 0: aload_0
  6. 1: aload_1
  7. 2: putfield #1 // Field this$$$$0:LInnerTest;
  8. 5: aload_0
  9. 6: invokespecial #2 // Method java/lang/Object."<init>":
  10. ()V
  11. 9: return
  12. public int getI();
  13. Code:
  14. 0: bipush 10
  15. 2: ireturn
  16. }

複制代碼 6: invokespecial #2 // Method java/lang/Object."<init>" 可以看到确實是調用的Object的構造函數。

是以,就算匿名内部類沒有名字,編譯器還是會自動為其加上一個名字,然後用它的父類構造函數去将其初始化。

---------------------- <a href="http://edu.csdn.net" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="blank">ASP.Net+Android+IO開發S</a>、<a href="http://edu.csdn.net" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="blank">.Net教育訓練</a>、期待與您交流! ----------------------

繼續閱讀