浅谈java继承机制——通过super调用父类方法

最近在看代码的时候遇到一个天坑,由于习惯性思维,可能大部分人都会掉近这个坑,所以拿出来记录一下

子类使用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

点赞