最近在看代码的时候遇到一个天坑,由于习惯性思维,可能大部分人都会掉近这个坑,所以拿出来记录一下
子类使用super调用的父类方法里,再调用父类的方法
先来看一段代码(该段代码只是作为测试用,正常编码时类名一定要规范编写)
package supertetst;
/** * @Author: x1aolone * @Date: 2019/11/8 22:11 */
class father {
public void invoke () {
say();
}
public void say () {
System.out.println("father say");
}
}
class child extends father{
@Override
public void invoke () {
super.invoke();
}
@Override
public void say () {
System.out.println("child say");
}
}
public class test {
public static void main(String[] args) {
child c = new child();
c.invoke();
}
}
猜猜上面的代码运行结果是什么?
“child say”
你没有看错,答案不是想象之中的”father say”,实际上调用的是子类中的say方法。
在刚看到代码时,你可能想当然地以为代码的执行顺序是:子类invoke() => 父类invoke() => 父类invoke()
但是,通过debug断点发现,上面这段代码的执行顺序是:子类invoke() => 父类invoke() => 子类invoke()
OK,虽然上面的例子有点“毁三观”,但是你可能以为你已经明白super的使用方法了,那么再看看下面这个粒子
子类使用super调用的父类方法里,再调用父类的变量
package supertetst;
/** * @Author: x1aolone * @Date: 2019/11/8 22:11 */
class father {
public String a = "father";
public void invoke () {
System.out.println(a);
}
}
class child extends father{
public String a = "child";
@Override
public void invoke () { super.invoke(); }
}
public class test {
public static void main(String[] args) {
child c = new child();
c.invoke();
}
}
在这一段例子中,变量a是公共变量,可以由子类继承,于是父类的变量a会被子类的变量a隐藏,类似@Override
按照我们上一个例子得出的教训,子类通过super调用的父类invoke方法,在父类Invoke方法里再调用say方法,实际上调用的是子类的invoke方法,那么此时父类的invoke方法使用的变量a应该也是子类覆盖过的变量a,也就是输出”child”
很遗憾!结果是输出“father”,也就是父类自己的变量a
那么经过以上两个例子,我们可以发现:
子类通过super调用父类的invoke方法,在父类的invoke方法中,如果调用方法,则优先选择子类重写的方法,如果使用变量,则选择父类自己的变量(这里可没有优先二字)
对于不关心原理的人来说,你只需要看到这个坑,记住上面那句话,不要再想当然采坑就好了,对于喜欢问“为什么”的同学,我们下面就来聊聊它到底是为什么
成员变量的隐藏和方法重写
如果父类中存在一个可继承的变量,并且子类中存在同类型、同名的变量,那么父类的变量会被隐藏,称为成员变量的隐藏
如果父类中存在一个可继承的方法,并且子类中存在同名、同参数类型、同参数个数、同返回值的方法,那么父类的方法会被隐藏,成为方法重写
无论是变量还是方法,其实都只是被隐藏了而已,在《JAVA程序设计精编教程》中是这么说的:“如果子类想使用被隐藏的方法或成员变量,必须使用关键字super”
super关键字
在JAVA中,super关键字用来操作被隐藏的成员变量和方法,对于变量,使用方式如super.x,对于方法啊,使用方式如super.x()
到这里,JAVA的基础知识就复习得差不多了,需要注意的是,例子1和例子中在父类和子类中同时出现的变量、方法,其实只是在子类中被隐藏了而已,在访问时是需要通过super关键字才能访问的。
回到我们的例子,先看例子1,为了方便你们阅读,再贴一遍代码
package supertetst;/** * @Author: x1aolone * @Date: 2019/11/8 22:11 */
class father {
public void invoke () {
say();
}
public void say () {
System.out.println("father say");
}
}
class child extends father{
@Override
public void invoke () {
super.invoke