有两个让我不太明白的调用:
1.http://smiky.iteye.com/admin/blogs/996590
2.
package org.gerry;
public class Son extends Parent
{
public void test()
{
super.test();
System.out.println( this );
this.duo();
}
public void duo()
{
System.out.println("Son duo");
}
}
package org.gerry;
public class Parent
{
public void test()
{
System.out.println("Parent test...");
System.out.println( this );
this.duo();
}
public void duo()
{
System.out.println("Parent duo");
}
}
package org.gerry;
public class client
{
public static void main(String[] args)
{
new Son().test();
}
}
第二个的结果相信会有人跟我一样弄错,父类的test()方法内会调用那个方法?子类or父类的?
字节码先上:
parent的test方法
public void test();
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #21 // String Parent test...
5: invokevirtual #23 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream;
11: aload_0
12: invokevirtual #29 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
15: aload_0
16: invokevirtual #32 // Method duo:()V
19: return
LineNumberTable:
line 7: 0
line 8: 8
line 9: 15
line 10: 19
LocalVariableTable:
Start Length Slot Name Signature
0 20 0 this Lorg/gerry/Parent;
可以看出偏移量为16的指令为invokevirtual,说明这个方法是个多态调用,但是看下面的变量表,很显示这个this是Parent类型的,根据方法解析规则:(下面是常量表中的类方法表
#32 = Methodref #1.#33 // org/gerry/Parent.duo:()V)
方法的符号引用在指令调用之前替换成直接引用,先找到#1对应的Parent(不用说Parent早就换成了Parent.class对应的直接引用),即然有了Class对象,接着就是在该类中查找#33对应的NameAndType类型常量池的索引,说白了就是在类中找duo方法,找得到的话就换成方法的直接引用
照上面的分析,结果应该是调用父类的duo()才对,可为什么调用的是子类的duo呢?可能有人说,invokevirtual表示调用多态方法啊,但是我父类中有duo方法啊,就算多态也应该找我自己的啊,轮不到子类啊
出现这种情况,只能在这种情况才可能,就是Parent的本地变量表中的this的直接引用就是指向 main方法中new的那个Son,在这种情况下多态才能体现出来(只有 invokevirtual对应的操作数this是Son,它才会动态分派到Son的duo方法)
从下面的打印结果能看出这点:Son中的this与Parent中的this指向的是同一个地址
Parent test...
org.gerry.Son@2f995c9a
Son duo
org.gerry.Son@2f995c9a
Son duo
还有一点顺带提一下,不知道对不对,
super根本就屁用没有,只不过指定编译器执行父类的方法,下面是Son的test()的字节码:
public void test();
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #15 // Method org/gerry/Parent.test:()V
4: getstatic #17 // Field java/lang/System.out:Ljava/io/PrintStream;
7: aload_0
8: invokevirtual #23 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
11: aload_0
12: invokevirtual #29 // Method duo:()V
15: return
LineNumberTable:
line 7: 0
line 8: 4
line 9: 11
line 10: 15
LocalVariableTable:
Start Length Slot Name Signature
0 16 0 this Lorg/gerry/Son;
从偏移为1的指令看出,它就是调用父类的test方法,而方法在执行的时候会传一个隐藏的this作为参数(从实际方法的本地方法表中可以看出),是不是这里是将Son的对象传到了Parent的test方法作为隐藏的this?估计也只有这个可能,
那么有什么方式可以获取到父类的对象呢?子类初始化时会初始化父类,那么生成的父类对象去那啦?可以调用到吗?如果调用不到的话在这种情况下算生成了几个对象?
对于上面第一个是不是也可以这样理解,对于属性只管其静态类型(本地变量表中的类型)?父类中的this的类型是父类本身,所以改奕的是父类的值,对子类无影响?
看来我是JS写多了,以为this是谁改变的就是谁的字段的值,走火入魔了
看来我的猜测是对的,将父类中的this强制改成子类,那么父类方法中操作的就是子类的属性了,看来对属性的操作只看静态类型
class A extends E{
int x;
int y;
public void setValue(int i,int j){
this.x = i;
this.y = j;
// B b = (B)this;
// b.x = i;
// b.y = j;
}
public int compute(){
return this.x * this.y;
}
}