正确的理解this 和 super

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在本质上是不同的。很简单的两行代码就说明问题了:

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

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

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

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

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();
        }
}  

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

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了吧!

时间: 2024-10-11 13:28:46

正确的理解this 和 super的相关文章

parent,son深刻理解this,super关键字

核心点: super关键字,表示调用的是父类对象. this关键字,表示调用的是当前运行类对象. 那么如果在父类中,使用了关键字,this,此时的this指的是什么呢? 看下面的例子,再加深理解核心店的第二句话就ok了. parent类: package com.ghsy.thissuper; public class Parent { public void init(){ System.out.println(this.getClass()); //获得当前运行类 System.out.pr

如何正确的理解和解决 ORA-01843:not a valid month

今天码代码的时候遇到了这个问题,因为oracle用的比较少,所在查询了一下. 顿时傻眼,有很多的贴子说是因为nls_date_language的问题,还要改会话级的NLS_DATE_LANGUAGE设置为简体中文,还有些别的,等等.我当时就无语了,我觉得大部分楼主都是在自己玩玩oracle的吧,虽然也算是因素,但如果是在正经项目中,谁会让你去改这种东西?! 后来发现了正确的做法,如下: to_date('2014-06-24 00:00:00','yyyy-mm-dd hh24:mi:ss')

深入,全面正确的理解Android.mk

Android.mk是NDK项目的必备组件,但是这里面的语法并不是那么好理解,因为官网上并没有说. LOCAL_PATH := $(call my-dir)  在Android.mk文件的第一句,必须有,宏功能,返回当前目录. include $(CLEAR_VARS) 清除了LOCAL_PATH 以外的LOCAL _<name>变量,讨厌的是不是全部,是LOCAL_PATH  以外的,这是因为LOCAL_<name> 是全局变量,清除他们可以避免冲突, LOCAL_MODULE

(转载)新手如何正确理解GitHub中“PR(pull request)”中的意思

我从知乎看到的两个答案,分别从实际意义以及语言学角度告诉你改怎么理解PR,很简洁,这个理解非常棒,会解决新手刚看到PR(pull request)这个词时的困惑. 实际意义: 有一个仓库,叫Repo A.你如果要往里贡献代码,首先要Fork这个Repo,于是在你的Github账号下有了一个Repo A2,.然后你在这个A2下工作,Commit,push等.然后你希望原始仓库Repo A合并你的工作,你可以在Github上发起一个Pull Request,意思是请求Repo A的所有者从你的A2合

正确理解 SqlConnection 的连接池机制[转]

作者: eaglet 转载请注明出处 .net 中通过 SqlConnection 连接 sql server,我们会发现第一次连接时总是很耗时,但后面连接就很快,这个其实和SqlConnection 的连接池机制有关,正确的理解这个连接池机制,有助于我们编写高效的数据库应用程序. 很多人认为 SqlConnection 的连接是不耗时的,理由是循环执行 SqlConnection.Open 得到的平均时间几乎为0,但每次首次open 时,耗时又往往达到几个毫秒到几秒不等,这又是为什么呢? 首先

IM开发基础知识补课(四):正确理解HTTP短连接中的Cookie、Session和Token

本文引用了简书作者“骑小猪看流星”技术文章“Cookie.Session.Token那点事儿”的部分内容,感谢原作者. 1.前言 众所周之,IM是个典型的快速数据流交换系统,当今主流IM系统(尤其移动端IM)的数据流交换方式都是Http短连接+TCP或UDP长连接来实现.Http短连接主要用于从服务器读取各种持久化信息:比如用户信息.聊天历史记录.好友列表等等,长连接则是用于实时的聊天消息或指令的接收和发送. 作为IM系统中不可或缺的技术,Http短连的重要性无可替代,但Http作为传统互联网信

python super()

一.问题的发现与提出 在Python类的方法(method)中,要调用父类的某个方法,在Python 2.2以前,通常的写法如代码段1: 代码段1: class A: def __init__(self): print "enter A" print "leave A" class B(A): def __init__(self): print "enter B" A.__init__(self) print "leave B"

关于Python的super用法研究

一.问题的发现与提出 在Python类的方法(method)中,要调用父类的某个方法,在python 2.2以前,通常的写法如代码段1: 代码段1: class A:  def __init__(self):   print "enter A"   print "leave A" class B(A):  def __init__(self):   print "enter B"   A.__init__(self)   print "

对白噪声的理解

在信号处理中,信号与噪声是一对永远的对手.如果没有噪声的话,信号处理则要简单得多.可以说,正是因为现实环境中不可避免地存在噪声,才推动人们去不断地开发新的信号处理方法.信号处理发展的历史,在某种程度上可以看做是信号与噪声相互斗争的历史.信号处理的主要目标之一即是如何区分信号与噪声,或者说如何增强信号而抑制噪声. 经典数字信号处理最基本的假设之一即是噪声为高斯白噪声.对白噪声,常有一些不够正确的理解,比如常将白噪声等同于高斯噪声即是典型的一例.那么,从哪些角度出发,可以更好地理解噪声和白噪声呢?