无聊:正确的理解this 和 super

来源:互联网 发布:aix mount linux nfs 编辑:程序博客网 时间:2024/06/05 00:08

  this和super是Java的两个关键字。

    先明确一个问题,有人错误的认为它们是对象里的“属性”,这只能怪老师没有讲清楚计算机的本质了。因为计算机的处理器只能用指令去处理数据,像C语言之类的容易理解,就是一个个的方法调用,对数据进行处理。那面向对象语言,确实是用对象调用方法啊,怎么回事?

好办,编译器耍个花样,将对象当做方法的参数就是了。比如

class Test{

     public void test(){}

}

Test t = new Test();

t.test();

计算机怎么处理呢?

假设我们把以上代码映射到面向过程的语言,大体是这个样子的:

void Test::test(final Test this){
}

当使用t.test();时,计算机实际处理为:

Test::test(t);//看到了吗?在方法中,this相当于形式参数,而调用方法的对象是实际参数。

也就是说this是实例方法中的第一个参数。对于static的方法,因为没有这个参数,所以就不能使用this了。

    this和super的基本功能这里不想探讨,只想说明一下它们在本质上的差别。

    有提法分别称之为this引用和super引用,为了表达的方便,这么说也无可厚非。但是从本质上来说,super并非一个引用,仅仅是一个起到指示作用的关键字而已,这与this在本质上是不同的。很简单的两行代码就说明问题了:

view plaincopy to clipboardprint?
class Main {  
 
    public void test() {  
        System.out.println(this);  
        System.out.println(super);  
    }  
}  
class Main {

    public void test() {
        System.out.println(this);
        System.out.println(super);
    }

    编译报错,super后面缺少.。也就说,至少在Java语言看来,super并不是一个合法的引用,而显然this是可以的。

    为了表达的方便,我们仍然称super为"super引用"。这种意义下,super引用和this引用有什么差别吗?差别当然有,最重要的是,类型不同,super的类型为父类类型引用,而this为当前类类型的引用。相同点呢?那就是这两个都是指向“当前对象”。不是说“父类对象”吗?啥,哪来的“父类对象”这个概念啊?

    言多必失,还是看代码吧。

 view plaincopy to clipboardprint?
class Base {  
 
    int i = 5;  
 
    public void test() {  
        System.out.println("In Base:" + this.i);  
    }  
}  
 
public class Test extends Base {  
 
    int i = 55;  
 
    public void test() {  
        //System.out.println(super);  竟然编译都不让通过  
        System.out.println(this); //正确,调用toString()方法  
        System.out.println(super.i);  
        System.out.println(this.i);  
        super.test();  
    }  
 
    public static void main(String[] args) {  
        new Test().test();  
    }  

class Base {

    int i = 5;

    public void test() {
        System.out.println("In Base:" + this.i);
    }
}

public class Test extends Base {

    int i = 55;

    public void test() {
        //System.out.println(super);  竟然编译都不让通过
        System.out.println(this); //正确,调用toString()方法
        System.out.println(super.i);
        System.out.println(this.i);
        super.test();
    }

    public static void main(String[] args) {
        new Test().test();
    }
}

 

     看出什么了?没有。没有就对了,反汇编。

view plaincopy to clipboardprint?
public void test();  
  Code:  
   0:   getstatic   #3; //Field java/lang/System.out:Ljava/io/PrintStream;  
   3:   aload_0  
   4:   invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V  
   7:   getstatic   #3; //Field java/lang/System.out:Ljava/io/PrintStream;  
   10:  aload_0  
   11:  getfield    #5; //Field Base.i:I  
   14:  invokevirtual   #6; //Method java/io/PrintStream.println:(I)V  
   17:  getstatic   #3; //Field java/lang/System.out:Ljava/io/PrintStream;  
   20:  aload_0  
   21:  getfield    #2; //Field i:I  
   24:  invokevirtual   #6; //Method java/io/PrintStream.println:(I)V  
   27:  aload_0  
   28:  invokespecial   #7; //Method Base.test:()V  
   31:  return 
 
public static void main(java.lang.String[]);  
  Code:  
   0:   new #8; //class Test  
   3:   dup  
   4:   invokespecial   #9; //Method "<init>":()V  
   7:   invokevirtual   #10; //Method test:()V  
   10:  return 
public void test();
  Code:
   0: getstatic #3; //Field java/lang/System.out:Ljava/io/PrintStream;
   3: aload_0
   4: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
   7: getstatic #3; //Field java/lang/System.out:Ljava/io/PrintStream;
   10: aload_0
   11: getfield #5; //Field Base.i:I
   14: invokevirtual #6; //Method java/io/PrintStream.println:(I)V
   17: getstatic #3; //Field java/lang/System.out:Ljava/io/PrintStream;
   20: aload_0
   21: getfield #2; //Field i:I
   24: invokevirtual #6; //Method java/io/PrintStream.println:(I)V
   27: aload_0
   28: invokespecial #7; //Method Base.test:()V
   31: return

public static void main(java.lang.String[]);
  Code:
   0: new #8; //class Test
   3: dup
   4: invokespecial #9; //Method "<init>":()V
   7: invokevirtual #10; //Method test:()V
   10: return

    注意那几个aload_0,这可是获取this啊(前面交代了,this是实例方法的第一个参数,第一个参数当然位置是0了)!那访问this.i和super.i的差别在什么地方呢?在这个i上。

  10: aload_0
   11: getfield #5; //Field Base.i:I

这是获取Base定义的i

 20: aload_0
   21: getfield #2; //Field i:I

这是获取Test中定义的i

    简单的说,Base.i和i就是当前对象中两个名字不同的字段。

    有人要质疑了:瞎掰吧!如果super和this是同一个对象的引用,那super调用方法的时候不又多态了?这的确是个问题。不过Java的设计者考虑到这个问题了,在jvm中提供了不同的调用方法的指令,分别是invokevirtual、invokespecial、invokeinterface、invokedynamic等。通过super调用实例方法时,用的就是invokespecial指令。当然,java的设计者们也不是一开始就考虑这么多的,invokespecial这个指令就是后来才加上的。事后诸葛亮?呵呵。

    再看一下上面的反编译代码:

   27: aload_0
   28: invokespecial #7; //Method Base.test:()V

//用invokespecial指令避免此时发生多态调用

而多态方法调用时用什么指令呢?

看main方法是如何调用test方法的吧:public static void main(java.lang.String[]);

7: invokevirtual #10; //Method test:()V

为什么叫virtual呢?C++里有个叫做虚函数的东西,难道……

     其实我们可以形象的说明一下,假设有两个变量_super和_this。

Test _this = new Test();

Super _super = _this;

    这样就能更清晰的区分this和super了吧?看了这点再结合开头对this参数的本质理解一下吧。

   对,还有个什么“父类对象”的问题。或许Test对象的布局是这个样子的:

 

    

   

传说中的“父类对象”或许是指Base.i这个东西吧。我不知道没有了mark字段和Klass指针的几个成员是否有资格叫做“对象”。

Sun hotspot jvm中的对象布局可以见下图:

 

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/ZangXT/archive/2009/09/24/4587985.aspx