深入java虚拟机-super庐山真面目

来源:互联网 发布:java内存泄露如何解决 编辑:程序博客网 时间:2024/06/07 01:46
  有想对super追根究底的冲动完全是因为下面这个问题。
  下面程序的输出结果是多少?
 import java.util.Date;
 public  class Test extends Date{
 public static void main(String[] args) {
 new Test().test();
 }

 public void test(){
 System.out.println(super.getClass().getName());
 }
 }
     这里程序的结果应该是Test。getClass方法是干嘛用的?api文档释义,返回一个对象的运行时类。那说明调用getClass()方法的这个对象引用指向的实例是Test类型的 。那么是否说明super指向了一个Test类型的实例呢??这里强调一点的是,其实super根本就不是一个对象的引用。也更加不是当前类父类型的的一个引用。(而this则不然,this确实是当前对象的一个引用)那getClass()方法调用是哪个对象引用发出的呢?其实还是this!也就是说其实super.getClass()的本质是this.(super.getClass())(当然了这个语句是无法编译通过的。),那这个super到底是怎么工作的呢。
     其实这里不通过虚拟机指令还是无法说明super本质的。
     java虚拟机的指令集里其中有两种方法调用指令如下(一共有四种方法调用指令)
   invokespecial,invokevirtual.
   java程序员都知道支持多态是java的一个重要特性,其实具体到指令集上,是
invokevirtual指令支持多态的!而invokespecial是不支持多态的!
   而我们通过使用super关键字发出方法调用时,指令就被编译器编译成了
invokespecial指令(发出方法调用的对象依然是this指向的实例)
比如下面一段代码
public class Parent {
public void test(){
}
        public void test1() {
test();
}
public class Child extends Parent {
public void test() {
super.test();
}
       public static void main (String[]args){
             new Child().test();
             new Child().test1();
        } 
}
其中Child的Test方法体编译为如下内容
aload_0
invokespecial #15(superdemo/Parent.test) 
return
其中aload_0是将this这个引用压入栈。(也证明了发出方法调用的依然是this)
#15指向常量池中索引值为15的常量,是一个CONSTANT_Methodref_info类型的常量。其代表
superdemo包中Parent类的test方法(这里这两个类所在包为superdemo)
因为invokespecial指令不支持多态,所以super.test方法调用的就是Parent中的test方法,(如果Parent中没有test方法,会去Parent父类里找)
再来看一下Parent中test1的方法编译后如下:
aload_0
invokevirtual #16(superdemo/Parent.test) 
return
 
那么new Child().test1()执行时,因为其使用的是invokevirtual指令,即支持多态,那么实际此时,test1()中的test()是调用的Child类的test()方法。

总结,super并不是一个对象引用(System.out.println(super)编译无法通过,也可以猜想到super的特殊之处),而this是!经过编译后,根本就没有super这个东西,通过super调用父类方法时,依然是this发出的。super被编译器编译为不支持多态的指令,从而可以调用父类的方法,(否则,上面的例子将会陷入递归死循环了。)

再记录一点关于this的心得。
  public class Parent {
public void test(){
System.out.println(this.getClass());
}

}
public class Child extends Parent {
public void test() {
super.test();
System.out.println(this.getClass());
}
}

public class SuperTest {
public static void main(String[] args) throws CloneNotSupportedException {
Child child=new Child();
child.test();
}
}

程序的输出结果是
class superdemo.Child
class superdemo.Child
证明子类父类里的this引用此时指向的均是Child的实例!解释 child.test()发出调用,那么此时,Child的test方法里,this==child,上面已经说过,super.test(),其实发出调用的是this.那么Parent的this==Child的this==child;
原创粉丝点击