学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的构造方法,并把两个参数传了进去。
这样一切都清楚了。
结论:其实匿名内部类也没有什么特别的地方,编译之后它有了名字,有了构造方法,就是一个正常的类了。
有理解不对的地方,请大家指正。
因为匿名内部类没有名字这个特殊性质,所以我们无从给它指定构造方法,构造方法必须和类名同名,类名都没有,构造方法就无从谈起了。但是匿名内部类可以通过直接调用父类的构造方法实现初始化,当然要求父类构造方法对它父类中定义的成员变量进行初始化。这里用一个例子看创建匿名内部类的时候父类的构造方法到底是如何调用的。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的构造方法,并把两个参数传了进去。
这样一切都清楚了。
结论:其实匿名内部类也没有什么特别的地方,编译之后它有了名字,有了构造方法,就是一个正常的类了。
有理解不对的地方,请大家指正。
解决方案 »
- 今天写了一个东西,运行的时候控制台提示了jvm有bug,因此散分以表示庆祝
- 100分求文中异常的解决方案-An unexpected error has been detected by Java Runtime Environment:
- 一个jdk的简单问题
- 一个面试题 大家进来看看 !
- 关于用java编写可执行文件的问题
- 有没有什么系统方法能够将null转成空字符串"",而不是"null"?
- 高分!!!think in java中的一道题(第八章27题),请高手帮助作一下。
- 新开JAVA学习QQ群组,请尽快加入!
- 在线程的执行过程中,若其执行时间已经超时,如何取消其的执行???
- applet被限制调用系统资源,那么applet打印问题该如何解决,高手进来讨论
- 100分:JAVA如何修改用户HTTP请求?
- short s1 = 1; s1 = s1 + 1; (s1+1运算结果是int型,需要强制转换类型)
我觉得匿名类有一个匿名的构造方法,而这个特殊的构造方法将通过特殊的方式去调用基类的构造函数(就是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嘛。(我是这么认为的)
这句,实际上new Text(x)调用的时候,编译器一方面在构造这个匿名的,Test类的子类;另一方面也在生成创建这个匿名类的对象实例的代码。所以,根据Java的后期动态绑定原则,实际上new test()调用了匿名子类的构造方法。
//这里将进行转换
Public Test get(int x){return new Test$1(x);}
//而匿名类代码里形式是 new Test(x) 其实是一样的,只不过这是一个隐式的操作
我在代码里说过了,它有名字,所以这里其实就是
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);//这样初始化并调用父类的构造方法
}
}
;
}
}
{ public int getI()
{
return super.getI() * 10;
} final InnerTest this$0; InnerTest$1(int x0)
{
this$0 = InnerTest.this;
super(x0);
}
}
我学的时间也不长,可能理解不够。
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);
}
}
;
}
}
这是内部类的一个语法。
2、InnerTest.this;指的是 InnerTest 的对象吗?
内部类如果要对外部类中的变量进行访问的话,不需要必须声明为final吧,
可否写个例子,偶想看看是怎么用的
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();
}
定义在方法中的内部类叫局部内部类,该类只能访问被final修饰的成员变量和参数