天天看点

黑马程序员 匿名内部类连名字都没有,会有构造函数么?

---------------------- <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>、期待与您交流! ----------------------

继续阅读