1 public class Parent { 2 protected String name = "parent"; 3 public Parent() { 4 System.out.println(this.name);//parent 5 fun();//son name: son 6 } 7 8 protected void fun() { 9 System.out.println("parent name: " + name); 10 } 11 } 12 13 public class Son extends Parent{ 14 protected String name = "son"; 15 public Son() { 16 } 17 18 protected void fun() { 19 System.out.println("son name: " + name); 20 } 21 }
实例化一个Son对象,会发现输出
parent son name: son
也就是说Parent的构造函数中,this.name的this为Parent,this.fun()的this为Son,这是为什么呢?其实Parent中的this始终都是Son,但由于字段的取值取决于实例的静态类型,而不是向invokevirtual那样取决于实际类型,我们都知道java方法的第一个参数永远是this(除了静态方法,this的作用就是连接实例对象,从而操作实例字段、实例方法,而静态方法并不会与某个实例关联,故静态方法不会传入this,也就不能操作实例字段、实例方法了),当Son构造器调用Parent构造器时传入的this,一定经过了类似向上转型的过程(猜测是这样),从而this的静态类型在Parent看来是Parent类型的。
这就解释了为什么在Parent中的this.name是Parent的字段,那么this.fun()是Son的怎么解释呢?这就是invokevirtual的威力了,invokevirtual会在运行时动态判断调用者的实际类型,从而决定调用方法的版本。
1 public class Parent { 2 protected String name = "parent"; 3 4 protected void fun() { 5 System.out.println("parent name: " + name); 6 fun(); 7 } 8 } 9 10 public class Son extends Parent{ 11 protected String name = "son"; 12 private boolean flag = false; 13 14 protected void fun() { 15 if(!flag) { 16 flag = true; 17 super.fun(); 18 }else{ 19 System.out.println("son name: " + name); 20 } 21 } 22 }
再来看上面这个例子,new Son().fun()一定是输出
parent name: parent son name: son
因为super.fun()会传入向上转型的this,从而在进行字段输出时输出parent,调用方法fun()时又会使用invokevirtual指令寻找实际类型,所以会调用Son的fun()方法,而此时传入的this又会向下转型为Son,故输出字段为son。