学Java 虽然时间不算太长,但是对一些原理性的东西很感兴趣。今天分析了一下匿名内部类调用构造方法的原理,希望高手拍砖。
    因为匿名内部类没有名字这个特殊性质,所以我们无从给它指定构造方法,构造方法必须和类名同名,类名都没有,构造方法就无从谈起了。但是匿名内部类可以通过直接调用父类的构造方法实现初始化,当然要求父类构造方法对它父类中定义的成员变量进行初始化。这里用一个例子看创建匿名内部类的时候父类的构造方法到底是如何调用的。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文件,这里只需要关注InnerTest.class 和 InnerTest$1.class。这里InnerTest$1.class是匿名内部类的class文件,InnerTest.class是InnerTest类的class文件。我们先看InnerTest$1.class的内容: 
javap -c InnerTest$1 > InnerTest$1.txt
得到代码如下 :Compiled from "Main.java"
class InnerTest$1 extends Test{
final InnerTest this$0;InnerTest$1(InnerTest, int);
  Code:
   0: aload_0
   1: aload_1
   2: putfield #1; //Field this$0:LInnerTest;
   5: aload_0
   6: iload_2
   7: invokespecial #2; //Method Test."<init>":(I)V
   10: returnpublic int getI();
  Code:
   0: aload_0
   1: invokespecial #3; //Method Test.getI:()I
   4: bipush 10
   6: imul
   7: ireturn}
很明显,我们的匿名内部类有了名字 InnerTest$1 ,而且是继承自 Test
class InnerTest$1 extends Test
这个类中有一个成员final InnerTest this$0;我想这应该是该内部类所在的外部类InnerTest的引用
这个匿名内部类的构造方法是:
InnerTest$1(InnerTest, int);
一个是InnerTest类型,也就是该类外部类的引用,调用的时候应该是把外部类对象的this指针传给它,这样就内部类就可以直接访问外部类的成员了。
另一个就是int类型的,应该是对i进行初始化用的。
看到下面这行:
7: invokespecial #2; //Method Test."<init>":(I)V
现在应该清楚了,这是调用了父类Test的构造方法。
下面再看InnerTest是如何实现的:Compiled from "Main.java"
class InnerTest extends java.lang.Object{
InnerTest();
  Code:
   0: aload_0
   1: invokespecial #1; //Method java/lang/Object."<init>":()V
   4: returnpublic Test get(int);
  Code:
   0: new #2; //class InnerTest$1
   3: dup
   4: aload_0
   5: iload_1
   6: invokespecial #3; //Method InnerTest$1."<init>":(LInnerTest;I)V
   9: areturn}InnerTest的get方法是关键。
这里首先new InnerTest$1,后面调用了InnerTest$1的构造方法,并把两个参数传了进去。
这样一切都清楚了。
结论:其实匿名内部类也没有什么特别的地方,编译之后它有了名字,有了构造方法,就是一个正常的类了。
有理解不对的地方,请大家指正。

解决方案 »

  1.   

    努力看完了第一个CODE,其余的没心情去看了,有高手给讲一下就好了
      

  2.   

    我想了很长时间,最后的结论是。其实我疯想出来的似乎就是正是正确的理解。
    我觉得匿名类有一个匿名的构造方法,而这个特殊的构造方法将通过特殊的方式去调用基类的构造函数(就是super(object);将参数传到基类的构造方法里。注意:这里的我用object,是因为这里的参数类型是由JVM自动帮你生成的。),而且只要基类的构造函数形参和传进去的参数类型一致,那么JVM就能够自动完成这个操作。(自己疯想出来的,仅仅只为了自己逻辑上能够想通)。将我这个理论套到楼主的代码中:

    public Test get(int x) {
            return new /*1、创建匿名类,后面的Test先放放*/ Test(x) /*3、这里将new Test$1()转型成 Test类型,至于这里写成Test(x)的形式,仅仅只是个语法形式而已*/{  
                @Override
                public int getI() {  //2、从这里开始和普通类一样进行初始化
                /*这里应该有个匿名的构造方法 
                 Test$1(int x){  //这里之所以是int类型是因为,JVM自动设置的
                   Super(x);
                 }*/
                    return super.getI() * 10;
                }
      因为如果先 new Test(x) 调用 Test 类的构造方法的话,那么逻辑上不对了,哪有先调用另一个类的构造方法,然后再硬插回来继续构造另一个类的,根据楼主的解说,其实这个匿名的构造函数还是有一个名字的,既然是一个有名的类,那一定会有一个构造方法,我想Java里应该不会有真正没有构造方法的类的吧,毕竟所有的类都继承Object嘛。(我是这么认为的)

      

  3.   

    嗯。像return new Test(x) {
    这句,实际上new Text(x)调用的时候,编译器一方面在构造这个匿名的,Test类的子类;另一方面也在生成创建这个匿名类的对象实例的代码。所以,根据Java的后期动态绑定原则,实际上new test()调用了匿名子类的构造方法。
      

  4.   

    其实我已经晕了,补充下如果将楼主的代码以普通代码方式写出来就更清楚一些了
    //这里将进行转换
    Public Test get(int x){return new Test$1(x);}
    //而匿名类代码里形式是 new Test(x) 其实是一样的,只不过这是一个隐式的操作
      

  5.   

    首先要注意jvm是负责运行.class文件的,别的都不是它负责。具体生成什么样的class文件是java编译器的事情,可以认为是javac这个命令执行的功能。
    我在代码里说过了,它有名字,所以这里其实就是
    new InnerTest$1(x);
    我也贴过InnerTest$1的类定义,编译器会根据匿名内部类的语法生成这些定义。
    所以里面构造方法是一个接受两个参数的方法,我们能看到。刚才用jad把class文件解析了一下,得到的源代码是这样的:
    class InnerTest
    {    InnerTest()
        {
        }    public Test get(int x)
        {
            return new Test(x) {            public int getI()
                {
                    return super.getI() * 10;
                }            final InnerTest this$0;            
                {
                    this$0 = InnerTest.this;
                    super(x0);//这样初始化并调用父类的构造方法
                }
            }
    ;
        }
    }
      

  6.   

    InnerTest$1class InnerTest$1 extends Test
    {    public int getI()
        {
            return super.getI() * 10;
        }    final InnerTest this$0;    InnerTest$1(int x0)
        {
            this$0 = InnerTest.this;
            super(x0);
        }
    }
    我学的时间也不长,可能理解不够。
      

  7.   

    有点没看明白
    class InnerTest
    {    InnerTest()
        {
        }    public Test get(int x)
        {
            return new Test(x) {            public int getI()
                {
                    return super.getI() * 10;
                }            final InnerTest this$0; //这里的 InnerTest this$0; 指的是InnerTest的对象?为什么还是final?            
                {
                    this$0 = InnerTest.this;  //InnerTest.this我觉得这里才指的是InnerTest的对象
                    super(x0);
                }
            }
    ;
        }
    }
      

  8.   

    内部类如果要对外部类中的变量进行访问的话,外部类中相应的变量必须声明为final.
    这是内部类的一个语法。
      

  9.   

    1、那 InnerTest this$0 指的是什么? 
    2、InnerTest.this;指的是 InnerTest 的对象吗?
      

  10.   

    就是把外部类对象的this传到当前类中来,这样就可以直接访问外部类中的成员了。不用搞这么细致。其实这些知识没有用。
      

  11.   

    无聊的时候就自己按 bytecode
      

  12.   

    ZangXT老师的无私精神真值得大家学习啊。这个
    内部类如果要对外部类中的变量进行访问的话,不需要必须声明为final吧,
    可否写个例子,偶想看看是怎么用的
      

  13.   

    public class Test {
        public Super getInstance(){
            int a=5;
            final int b=6;
            class Inner implements Super{            public void print() {
                    //内部类中访问外面定义的局部变量
                    System.out.println(a);//编译错误:Test.java:9: 从内部类中访问局部变量 a;需要被声明为最终类型
                    System.out.println(b);
                }
                
            }
            return new Inner();
        }
        public static void main(String[] args) {
            Super s=new Test().getInstance();
            s.print();
        }
    }
    interface Super{
        public void print();
    }
      

  14.   

    定义在类中的内部类分为实例内部类和静态内部类,实例内部类自动持有外部类的实例的引用,即可以访问外部类的所有变量;静态内部类可以直接访问外部类的静态成员
    定义在方法中的内部类叫局部内部类,该类只能访问被final修饰的成员变量和参数