zxbyhcsdn

 

关于this和super有必要理解清楚的地方

关于this和super又必要理解清楚的地方!重要的,多态的关键

二话不说,先来一段代码,比较短,需要付出一点点耐心(估计一上来就是代码,又会赶走很多人吧.....)

//父类
class FatherClass {
    public int value;
    public void f() {
        this.run();//注意这儿的是this,注意
    }
    public void run(){
        System.out.println("FatherClass.run():"+this.value);
    }
}

//子类
class ChildClass extends FatherClass {
    public int value = 100;
    public void ff() {
        super.run();//注意这儿的是super,注意
    }
    //重写
    public void run(){
        System.out.println("ChildClass.run():"+super.value);
    }
}

//孙子类,这儿用了一个拼音,偷懒一下,记不到那个孙子的单词了。寒啊...当初还过四级,现在忘光光
class SunziClass extends ChildClass {
    public int value = 200;
    //再次重写
    public void run() {
        System.out.println("SunziClass.run():"+super.value);
    }
}

//这儿是调用的Main
public class TestInherit {
      public static void main(String[] args) {
                FatherClass fc = new ChildClass();
                fc.f();
                FatherClass fc1 = new SunziClass();
                fc1.f();
                ChildClass cc = new SunziClass();
                cc.ff();
     }
}

想一想运行结果,注意那个this和super。有点晕头把,不要晕,稳住!自己想一下结果,然后再来看运行结果。

运行结果如下:
ChildClass.run():0;
SunziClass.run():100;
FatherClass.run():0;

估计有不少人会大吃一惊!其他不吃惊的人可以散了!吃惊的人继续看下面的。

this代表什么?这儿代表New出来的那个玩意儿在堆空间的引用地址
对于下面的这一句:
FatherClass fc1 = new SunziClass();
fc1这个变量里面放的就是this,是new SunziClass()这个玩意儿在堆空间的引用地址.
只是这儿fc1是定义成FatherClass,只有FatherClass的属性和方法是可见的,不过这不是本文讨论的重点.

所以说在父类里面写的this其实就是这个父类或者他的子类,孙子类,曾孙子类... 在运行的时候[重复一次哈,运行的时候runtime], New 子类()或者New 孙子类()或者New 曾子类()或者....的堆空间的引用地址.
[再重复一次,FatherClass里面的this,子类里面的this...都是一样的,指向New出来的那玩意儿的引用地址]

很神奇哈!!嘿嘿,这个其实就是 多态的体现,也是多态的实现基础.看父类或者接口定义了方法调用的逻辑,子类可以根据需要改变算法! 这样就可以将关注的方面分为两个层面,一:关注方法调用的业务逻辑[做什么],二:关注方法的实现[怎么做].  "做什么和怎么做",这玩意儿好像在软件工程里面比较眼熟哦!!!!

问题分解了,而且可复用性也大大提高了.

好了,this总结完了,好像越说越晕了... 再稳一下,看看Super

下面再来说super
说之前,分析一下内存多,这个图,挖卡,蜘蛛网来也....




现在看这两句:
ChildClass cc = new SunziClass();
cc.ff();
这儿SunziClass里面没有重写父类ChildClass的ff方法,
所以是执行的父类ChildClass的ff方法.这个方法里面有一个super.Run();
这儿实际上就执行到了FatherClass的Run()方法.

这说明什么啦.
说明super只是记录对象内部的父类的特征(属性和方法)的一个引用
(注意哈:这儿super不是指向父类对象的引用地址哈,new出来的玩意儿才在堆里面分配空间有引用地址,这儿没有去new一个父类对象哈,只是执行了父类的构造函数将父类的特征生成了,但是属于New出来的那个子类对象)
估计上面这段话也够晕人了...

实践一下,你可以写
public FatherClass getThis()
{
    return this;
}
编译通过,没有任何问题
但是你写
public FatherClass getSuper()
{
    return super;
}

嘿嘿,编译出错! 为什么啦,
因为super并不是一个堆空间里面的一个对象的引用地址,而this才是堆空间里面的一个对象的引用地址
    super只能在对象内部使用,而this可以在对象内部使用也可以返回出对象外.
    super是死的,编译的时候就定死了super的指向了,而this是活的,在运行时候决定其指向.

再说一下,子类实例化对象,并没有去实例化他的父类对象,也就是说,那个子类对象里面并没有一个父类对象,
那你说没有父类对象,为什么子类构造函数要执行父类的构造函数啦,那是因为需要创建父类的特征赋予子类,但是是由子类所有,而super就是用来区别是是否是父类对象的特征的.
重写父类方法属性,就是再创建了一个子类的特征,当你用this的时候,就覆盖了父类的特征了,但是父类特征还在那儿,用super就能访问道,但是只能在对象的内部使用.对象外面就只能看到覆盖了父类特征的子类特征了.

写完收工,我Cao,写了2个小时.....
其实写了这么一大堆,就是一句:"this是当前对象在堆空间的引用地址,super是当前对象的父类特征的引用"

有什么不同的见解欢迎讨论...共同进步!!!

posted on 2008-07-21 15:35 zxbyh 阅读(1620) 评论(11)  编辑  收藏 所属分类: J2se

评论

# re: 关于this和super有必要理解清楚的地方 2008-07-21 16:34 隔叶黄莺

前面代码跨度太大,看了有些累,用
public void f(){
this.run();//注意这儿的是this,注意
}

能紧缩一下,看提眼不会这么浑。
Java中的实例方法都是虚的,调用实例方法总隐藏 this 引用。
其实就是要死咬住一点,不管调用在方法栈怎么来转来转去,this 代表着谁不能丢了就好理解了。  回复  更多评论   

# re: 关于this和super有必要理解清楚的地方 2008-07-21 16:42 zxbyh

好了,紧缩了,呵呵
  回复  更多评论   

# re: 关于this和super有必要理解清楚的地方 2008-07-21 23:34 fwy

ChildClass cc = new SunziClass();
cc.ff();
----------
最后执行到Father类里面的
public void run(){
System.out.println("FatherClass.run():"+this.value);
}
这里面的this是引用的Sunzi对象,为什么this.value却是Father的默认value值0呢?  回复  更多评论   

# re: 关于this和super有必要理解清楚的地方 2008-07-22 10:59 隔叶黄莺

这样解释吧,深入到虚拟机这一层,方法的调用常用以下两个指令:

invokevirtual
invokespecial

顾名思义,看到 virtual,应该能想到虚方法的调用,也就是动态绑定,以当前实例的真实类型来决定调用哪个实例的方法。

而 invokespecial,却比较古板,它是由实例的引用类型来决定调用哪个方法。

Java 中的实例初始化方法<init>,基本可以等同于 this,私有方法和 super所调用的方法是采用 invokespecial 指令的,也就是根据引用类型决定调用的目标方法。

其他更多时候是用 invokevirtual 指令,不细究这个了。希望以上的说明能帮助大家的理解。  回复  更多评论   

# re: 关于this和super有必要理解清楚的地方[未登录] 2008-07-22 13:41 zxbyh

ChildClass cc = new SunziClass();
cc.ff();
----------
最后执行到Father类里面的
public void run(){
System.out.println("FatherClass.run():"+this.value);
}
这里面的this是引用的Sunzi对象,为什么this.value却是Father的默认value值0呢?
------------------------------------------------------------
这儿实际上在Sunzi类里面是覆盖了父类的Public int value 成员变量了,一般来说应该设置成一个Private 成员变量,然后写getter和setter来访问.

一般不推荐直接覆盖父类的Public 成员变量,这个在C#里面是编译不能通过的.
Java里面能通过,但是出现这样匪夷所思的结果. 我也不知道是怎么回事情了,
难道调用方法的this和调用成员变量的this不是一个??好像不可能啊??
那位高手帮忙解释一下啦??  回复  更多评论   

# re: 关于this和super有必要理解清楚的地方 2008-07-22 14:36 zxbyh

困惑了半天,
然后看到这一篇文章顿时茅塞顿开:
http://www.sudu.cn/info/html/edu/jsp/20071109/102484.html

原来对象的成员变量在Java里面是允许override的.
但是成员变量是在编译的时候决定,但是方法是在运行时决定.
方法可以动态绑定,但是成员变量不可以动态绑定,如果要让成员变量实现动态绑定,那么就需要通过方法.

还是上面那段代码,在Main方法里面加上如下
FatherClass fc1 = new SunziClass();
fc1.f();
//加上下面的这一段话:
System.out.println(fc1.value);
System.out.println(((SunziClass)fc1).value);

运行结果是:
0
200


怪不得C#里面不允许Override成员变量
  回复  更多评论   

# re: 关于this和super有必要理解清楚的地方 2008-07-22 16:56 隔叶黄莺

很显然你应该规避这种问题的出现,既然父类中有一个公有的属性,子类就应该直接继承这个属性,再次声明一个同名的公有属性的意义何在。

这种问题可能也就能拿来做面试题,不过我找工作都不愿意做笔记题的,心里抵制这种面试方式。  回复  更多评论   

# re: 关于this和super有必要理解清楚的地方[未登录] 2008-07-22 21:31 zxbyh

个人还是觉得Java应该不允许override public成员变量.
直接让他编译不能通过还还好点...

脑袋想大了都想不出这样有何意义,既然是不规范的东西,何不尽早不让他发生.
就像C#那样,编译都通不过!!  回复  更多评论   

# re: 关于this和super有必要理解清楚的地方 2008-07-22 22:53 fwy

@zxbyh
你好,我还是没能理解最后
System.out.println("FatherClass.run():"+this.value);
this确实是Sunzi对象,this.value是如何找到Father里面的value的?

  回复  更多评论   

# re: 关于this和super有必要理解清楚的地方 2008-07-23 12:40 zhuxing

写的挺不错的,就是有点乱~_~
再重新组织一下思路  回复  更多评论   

# re: 关于this和super有必要理解清楚的地方 2008-07-25 00:58 stanleyxu

Your example makes beginners really confused. Because you showed a bad logic, why a FatherClass variable should create an ChildClass instance. I suggest you use vehicle, auto, suv, truck, etc.  回复  更多评论   


只有注册用户登录后才能发表评论。


网站导航:
 

导航

统计

常用链接

留言簿(1)

随笔分类

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜