重写:简单说来就是子类对父类方法的重新功能定义。所以重写一定要有继承,继承是重写的前提。但是重写有很多的规则,很多人都不一定能全部搞清楚,下面我们就来一一解释:
1、请看以下代码:
public class TestOverride { public static void main(String[] args) { Base base = new Sub(); base.print(); //此处错误 } } class Base { private void print(){ System.out.println("from Base"); } } class Sub extends Base { public void print(){ System.out.println("from Sub"); } } |
Sub类里的print()方法并不是对Base类的print()方法的重写,因为Base类里的print()方法是private的,即是私有的,在子类当中根本就无法访问到其私有方法。所以在上面的代码中,Sub类只是定义了一个和父类同名同参数的方法,但是这个方法却并不是重写,只是单纯的方法定义。
结论:私有方法不可重写。
2、请看以下代码:
public class TestOverride { public static void main(String[] args) { Base base = new Sub(); base.print(); } } class Base { public static void print(){ System.out.println("from Base"); } } class Sub extends Base { public static void print(){ System.out.println("from Sub"); } } |
以上代码的执行结果为:
from Base |
说明,Sub类的print()方法并不是对Base类中的print()方法的重写。因为Java在动态执行过程中的多态主要体现在实例方法而不是类方法上,对于对象的静态方法的执行,主要是根据对象的编译时类型来决定,而不是运行时类型来决定。
结论:类方法,即静态方法不可重写。
3、请看以下代码:
public class TestOverride { public static void main(String[] args) { Base base = new Sub(); base.print(); } } class Base { public void print(){ System.out.println("from Base"); } } class Sub extends Base { void print(){ //此处错误 System.out.println("from Sub"); } } |
因为父类中的print()方法的访问权限为public,而子类的print()方法的访问权限为default,所以此处子类对父类的重写是错误的。
结论:子类重写父类方法,子类中该方法的访问权限不能低于父类中该方法的访问权限。
访问权限的大小:public > protected > default > private
4、请看以下代码:
public class TestOverride { public static void main(String[] args) { Base base = new Sub(); base.print(); } } class Parent{} class Son extends Parent{} class Base { public Parent print(){ System.out.println("from Base"); return new Parent(); } } class Sub extends Base { public Son print(){ //重写正确 System.out.println("from Sub"); return new Son(); } } |
Java重写需满足的条件中有:子类方法和父类方法有相同的方法名称 相同的参数列表 一致的返回值类型。此处的相同指的是完全相同,即必须完全一样。而此处的一致,则指的是子类的方法返回值类型必须和父类的方法返回值类型相同或者是父类方法返回值类型的子类。
结论:子类重写父类方法,并不一定方法签名完全一样,返回值类型可以不一样,但必须兼容。
5、请看以下代码:
import java.io.FileNotFoundException; import java.io.IOException; public class TestOverride { public static void main(String[] args) throws Exception { Base base = new Sub(); base.print(); } } class Base { public void print() throws FileNotFoundException{ System.out.println("from Base"); } } class Sub extends Base { public void print() throws IOException{ //此处错误 System.out.println("from Sub"); } } |
以上Sub类重写print()方法错误,因为父类方法抛出FileNotFoundException,而子类方法抛出IOException,IOException比FileNotFoundException范围更广。
结论:子类重写父类方法时,如果父类抛出异常,则子类只能抛出范围不大于父类异常类型的异常,也可以不抛出异常。即子类如果抛出异常,只能抛出和父类抛出的异常同样的异常或者是父类异常类的子类。注意,此处指的是异常的范围,而不是异常的数量,请看以下代码:
6、
public class TestOverride { public static void main(String[] args) throws Exception { Base base = new Sub(); base.print(); } } class Base { public void print() throws IOException{ System.out.println("from Base"); } } class Sub extends Base { public void print() throws FileNotFoundException,EOFException{ //重写正确 System.out.println("from Sub"); } } |
虽然此处子类重写父类方法,子类中抛出了比父类数量更多的异常,但此处的重写依然正确,因为
FileNotFoundException和EOFException都是IOException的子类,他们两个的范围之和依然比IOException的范围要小,所以此处重写正确。
结论:子类可以抛出比父类数量更多的异常,但范围之和一定不能超过父类。
7、以上5、6点的结论中的异常均是指的checked异常,而不是runtime异常,请看以下代码:
import java.io.IOException; public class TestOverride { public static void main(String[] args) throws Exception { Base base = new Sub(); base.print(); } } class Base { public void print() throws IOException{ System.out.println("from Base"); } } class Sub extends Base { public void print() throws RuntimeException{ //重写正确 System.out.println("from Sub"); } } |
以上代码中,虽然子类中抛出了并不包含在父类异常范围内的异常,但因为子类抛出的是runtime异常,并不是checked异常,所以重写正确。实际上,runtime异常并不在重写规则的限定之内,也就是说,runtime异常,可以随意抛出。
结论:runtime异常不在重写规则限制之内,可以随意抛出。
8、请看以下代码:
public class TestOverride { public static void main(String[] args) throws Exception { Base base = new Sub(); System.out.println(base.i); } } class Base { public int i = 5; } class Sub extends Base { public int i = 10; } |
以上代码的执行结果为:
5 |
输出的是父类的属性值而不是子类的属性值,虽然子类和父类存在相同命名的属性并且值不一样,但是子类的属性值并没有覆盖父类的相同属性。
结论:重写只存在于实例方法之间,实例属性并不能被重写。如以上示例,子类的i属性只能算是对父类的i属性的隐藏。