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

本文探讨了Java中的匿名内部类如何在没有构造函数的情况下实例化对象。通过一个例子展示了匿名内部类继承父类时,编译器会自动生成带参数的构造方法,并调用父类的构造函数进行初始化。同时,文章提到了匿名内部类实现接口的情况,并解释了接口与继承的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

---------------------- <a href="http://edu.csdn.net"target="blank">ASP.Net+Android+IO开发S</a>、<a href="http://edu.csdn.net"target="blank">.Net培训</a>、期待与您交流! ----------------------

我在上一篇博客中说到匿名内部类,里面提到一句话,说匿名内部类是没有构造函数的,但是却可以实例化一个对象,这是百度的,我第一次看到这句话时楞了一下,为什么呢?没有构造函数,拿什么区创建实例呢?

查了查,原来是因为匿名内部类连名字的都没有,怎么会有构造函数呢,总不能写一对括号吧,后来想想内部类其实类似父子类,既然继承父类,肯定得调用父类的构造函数啦,于是我就在网上查了一篇博客,地址是http://zangxt.iteye.com/blog/421560,里面就举例说明了匿名内部类调用父类的构造函数,内容我就直接拿过来了:

Java代码   收藏代码
  1. package testtest;  
  2.   
  3. public class Main {  
  4.   
  5.     public static void main(String[] args) {  
  6.         InnerTest inner = new InnerTest();  
  7.         Test t = inner.get(3);  
  8.         System.out.println(t.getI());  
  9.     }  
  10. }  
  11.   
  12. class Test {  
  13.   
  14.     private int i;  
  15.   
  16.     public Test(int i) {  
  17.         this.i = i;  
  18.     }  
  19.   
  20.     public int getI() {  
  21.         return i;  
  22.     }  
  23. }  
  24.   
  25. class InnerTest {  
  26.   
  27.     public Test get(int x) {  
  28.         return new Test(x) {  
  29.   
  30.             @Override  
  31.             public int getI() {  
  32.                 return super.getI() * 10;  
  33.             }  
  34.         };  
  35.     }  
  36. }  

编译之后得到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.   
  5. testtest.InnerTest$1(testtest.InnerTest, int);  
  6.   Code:  
  7.    0:   aload_0  
  8.    1:   aload_1  
  9.    2:   putfield    #1//Field this$0:Ltesttest/InnerTest;  
  10.    5:   aload_0  
  11.    6:   iload_2  
  12.    7:   invokespecial   #2//Method testtest/Test."<init>〈init〉":(I)V  
  13.    10:  return  
  14.   
  15. public int getI();  
  16.   Code:  
  17.    0:   aload_0  
  18.    1:   invokespecial   #3//Method testtest/Test.getI:()I  
  19.    4:   bipush  10  
  20.    6:   imul  
  21.    7:   ireturn  
  22.   
  23. }  
  24.   
  25. </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.   
  9. public testtest.Test get(int);  
  10.   Code:  
  11.    0:   new #2; //class testtest/InnerTest$1  
  12.    3:   dup  
  13.    4:   aload_0  
  14.    5:   iload_1  
  15.    6:   invokespecial   #3; //Method testtest/InnerTest$1."<init>〈init〉":(Ltesttest/InnerTest;I)V  
  16.    9:   areturn  
  17.   
  18. }  
  19. </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">ASP.Net+Android+IO开发S</a>、<a href="http://edu.csdn.net"target="blank">.Net培训</a>、期待与您交流! ----------------------

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值