首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > JAVA > J2SE开发 >

菜鸟对java匿名内部类构造原理的分析 (散分)

2012-04-03 
初学者对java匿名内部类构造原理的分析(散分)学Java 虽然时间不算太长,但是对一些原理性的东西很感兴趣。今

初学者对java匿名内部类构造原理的分析 (散分)
学Java 虽然时间不算太长,但是对一些原理性的东西很感兴趣。今天分析了一下匿名内部类调用构造方法的原理,希望高手拍砖。
  因为匿名内部类没有名字这个特殊性质,所以我们无从给它指定构造方法,构造方法必须和类名同名,类名都没有,构造方法就无从谈起了。但是匿名内部类可以通过直接调用父类的构造方法实现初始化,当然要求父类构造方法对它父类中定义的成员变量进行初始化。这里用一个例子看创建匿名内部类的时候父类的构造方法到底是如何调用的。

Java code
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
得到代码如下 :
Java code
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是如何实现的:
Java code
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的构造方法,并把两个参数传了进去。
这样一切都清楚了。
结论:其实匿名内部类也没有什么特别的地方,编译之后它有了名字,有了构造方法,就是一个正常的类了。
有理解不对的地方,请大家指正。

[解决办法]

[解决办法]
jf
[解决办法]
学习

[解决办法]
jf,学习
[解决办法]
努力看完了第一个CODE,其余的没心情去看了,有高手给讲一下就好了
[解决办法]
jf
[解决办法]
jf..
[解决办法]
我想了很长时间,最后的结论是。其实我疯想出来的似乎就是正是正确的理解。
我觉得匿名类有一个匿名的构造方法,而这个特殊的构造方法将通过特殊的方式去调用基类的构造函数(就是super(object);将参数传到基类的构造方法里。注意:这里的我用object,是因为这里的参数类型是由JVM自动帮你生成的。),而且只要基类的构造函数形参和传进去的参数类型一致,那么JVM就能够自动完成这个操作。(自己疯想出来的,仅仅只为了自己逻辑上能够想通)。



将我这个理论套到楼主的代码中:

Java code
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;            }
[解决办法]
嗯。

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

[解决办法]
ZangXT老师的无私精神真值得大家学习啊。

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

[解决办法]
学习
[解决办法]
学习了
[解决办法]
路过,接点分
[解决办法]
嘿嘿

热点排行