一、java覆盖如何执行:编译看左边,运行看右边
在子类方法覆盖父类方法时,在编译期,编译器会检查这个对象的引用类型是否含有此方法。如果没有则编译会出错,有则会通过编译。但在执行期,JVM寻找的不是引用指向的类型,而是堆上的对象。(即编译看左边,运行看右边)。
1.子类方法覆盖了父类方法
//Father类
public class Father {
public void turnOn(){
System.out.println("父类方法turnOn执行");
}
}
//Son类,继承了Father类
public class Son extends Father {
@Override
public void turnOn() {
System.out.println("子类方法turnOn执行");
}
}
public class Demo {
public static void main(String[] args) {
Father f = new Son();
f.turnOn();
}
}
运行Demo类中的main()方法:
在编译期,编译器会检查对象f的引用类型(即Father)是否有turnOn(),由于Father类有这个方法,于是编译器不会报错,程序编译成功。在运行期,究竟运行Father类的两个方法还是Son类的方法呢?JVM会从对象f开始向上找直到找到turnOn()为止,由于我们在Son类覆盖了turnOn(),所以JVM会执行Son类的turnOn()。
2.父类引用不能调用子类特有的方法
//将上面的Son.class修改成下面这个
public class Son extends Father {
@Override
public void turnOn() {
System.out.println("子类方法turnOn执行");
}
//定义一个只有子类有父类没有的方法
public void sonFunction(){
System.out.println("子类独有方法");
}
}
修改Demo的main()为下图的代码,编译,可以看到编译器找不到sonFunction()。为什么呢?因为根据覆盖的原理,f的引用类型(Father)是没有sonFunction()方法的,所以就会编译报错。
3.父类引用调用子类特有的方法?利用instanceof或抽象类
下面只介绍instanceof,抽象类快忘光了,下次再补充...(反正应该没人看我的文章)。
将Demo的代码修改成下图:
public class Demo {
public static void main(String[] args) {
Father f = new Son();
f.turnOn();
if(f instanceof Son){
//对象A instanceof B,如果对象A是类B的对象,则返回true,否则返回false
Son s = (Son) f;//向下转型,将f由Father类的引用转为Son类的引用
s.sonFunction();//调用Son类独有的方法
}
}
}
二、java子类方法覆盖父类方法应该遵守的规则
1、参数必须要一样,且返回类型要兼容
父类方法使用了哪种参数,覆盖此方法的子类也一定要使用相同的参数。不论父类的声明的返回类型是什么,子类必须声明返回一样的类型或该类型的子类型。
2、不能降低方法的存取权限
这代表存取权限必须相同,或者更为开放。举例来说,你不能在子类中覆盖一个公有方法并将它标记为私有。这会让JVM在编译期以为通过的是个公有方法,然后在执行期才会被JVM阻止存取。
public class Father {
public void turnOn(){
System.out.println("父类方法turnOn执行");
}
}
public class Son extends Father {
@Override
private void turnOn() {//此处报错!!!!报错信息如下图
System.out.println("子类方法turnOn执行");
}
}