Overriding:嘿嘿,就不告诉你这 11 条规则

开发 后端
说回正题。重写(Overriding)算是 Java 中一个非常重要的概念,理解重写到底是什么对每个 Java 程序员来说都至关重要,这篇文章就来给大家说说重写过程中应当遵守的 11 条规则。

[[378816]]

哎呀妈呀,等老久了吧!关于 Java 方面的文章终于来了,快快快,扶寡人起来,还能再举——鼎(明眼人都能看的出来,我受大秦帝国之纵横天下这部剧的影响了)。

说回正题。重写(Overriding)算是 Java 中一个非常重要的概念,理解重写到底是什么对每个 Java 程序员来说都至关重要,这篇文章就来给大家说说重写过程中应当遵守的 11 条规则。

01、什么是重写?

重写带来了一种非常重要的能力,可以让子类重新实现从父类那继承过来的方法。在下面这幅图中,Animal 是父类,Dog 是子类,Dog 重新实现了 move() 方法用来和父类进行区分,毕竟狗狗跑起来还是比较有特色的。

重写的方法和被重写的方法,不仅方法名相同,参数也相同(也就说是方法签名相同),只不过,方法体有所不同。

02、哪些方法可以被重写?

规则一:只能重写继承过来的方法。

因为重写是在子类重新实现从父类继承过来的方法时发生的,所以只能重写继承过来的方法,这很好理解。这就意味着,只能重写那些被 public、protected 或者 default 修饰的方法,private 修饰的方法无法被重写。

Animal 类有 move()、eat() 和 sleep() 三个方法:

  1. public class Animal { 
  2.     public void move() { } 
  3.  
  4.     protected void eat() { } 
  5.      
  6.     void sleep(){ } 

Dog 类来重写这三个方法:

  1. public class Dog extends Animal { 
  2.     public void move() { } 
  3.  
  4.     protected void eat() { } 
  5.  
  6.     void sleep(){ } 

OK,完全没有问题。但如果父类中的方法是 private 的,就行不通了。

  1. public class Animal { 
  2.     private void move() { } 

此时,Dog 类中的 move() 方法就不再是一个重写方法了,因为父类的 move() 方法是 private 的,对子类并不可见。

  1. public class Dog extends Animal { 
  2.     public void move() { } 

03、哪些方法不能被重写?

规则二:final、static 的方法不能被重写。

一个方法是 final 的就意味着它无法被子类继承到,所以就没办法重写。

  1. public class Animal { 
  2.     final void move() { } 

由于父类 Animal 中的 move() 是 final 的,所以子类在尝试重写该方法的时候就出现编译错误了!

同样的,如果一个方法是 static 的,也不允许重写,因为静态方法可用于父类以及子类的所有实例。

  1. public class Animal { 
  2.     final void move() { } 

重写的目的在于根据对象的类型不同而表现出多态,而静态方法不需要创建对象就可以使用。没有了对象,重写所需要的“对象的类型”也就没有存在的意义了。

04、重写方法的要求

规则三:重写的方法必须有相同的参数列表。

  1. public class Animal { 
  2.     void eat(String food) { } 

Dog 类中的 eat() 方法保持了父类方法 eat() 的同一个调调,都有一个参数——String 类型的 food。

  1. public class Dog extends Animal { 
  2.     public void eat(String food) { } 

一旦子类没有按照这个规则来,比如说增加了一个参数:

  1. public class Dog extends Animal { 
  2.     public void eat(String food, int amount) { } 

这就不再是重写的范畴了,当然也不是重载的范畴,因为重载考虑的是同一个类。

规则四:重写的方法必须返回相同的类型。

父类没有返回类型:

  1. public class Animal { 
  2.     void eat(String food) { } 

子类尝试返回 String:

  1. public class Dog extends Animal { 
  2.     public String eat(String food) { 
  3.         return null
  4.     } 

于是就编译出错了(返回类型不兼容)。

规则五:重写的方法不能使用限制等级更严格的权限修饰符。

可以这样来理解:

  • 如果被重写的方法是 default,那么重写的方法可以是 default、protected 或者 public。
  • 如果被重写的方法是 protected,那么重写的方法只能是 protected 或者 public。
  • 如果被重写的方法是 public, 那么重写的方法就只能是 public。

举个例子,父类中的方法是 protected:

  1. public class Animal { 
  2.     protected void eat() { } 

子类中的方法可以是 public:

  1. public class Dog extends Animal { 
  2.     public void eat() { } 

如果子类中的方法用了更严格的权限修饰符,编译器就报错了。

规则六:重写后的方法不能抛出比父类中更高级别的异常。

举例来说,如果父类中的方法抛出的是 IOException,那么子类中重写的方法不能抛出 Exception,可以是 IOException 的子类或者不抛出任何异常。这条规则只适用于可检查的异常。

可检查(checked)异常必须在源代码中显式地进行捕获处理,不检查(unchecked)异常就是所谓的运行时异常,比如说 NullPointerException、ArrayIndexOutOfBoundsException 之类的,不会在编译器强制要求。

父类抛出 IOException:

  1. public class Animal { 
  2.     protected void eat() throws IOException { } 

子类抛出 FileNotFoundException 是可以满足重写的规则的,因为 FileNotFoundException 是 IOException 的子类。

  1. public class Dog extends Animal { 
  2.    public void eat() throws FileNotFoundException { } 

如果子类抛出了一个新的异常,并且是一个 checked 异常:

  1. public class Dog extends Animal { 
  2.    public void eat() throws FileNotFoundException, InterruptedException { } 

那编译器就会提示错误:

  1. Error:(9, 16) java: com.itwanger.overriding.Dog中的eat()无法覆盖com.itwanger.overriding.Animal中的eat() 
  2.   被覆盖的方法未抛出java.lang.InterruptedException 

但如果子类抛出的是一个 unchecked 异常,那就没有冲突:

  1. public class Dog extends Animal { 
  2.    public void eat() throws FileNotFoundException, IllegalArgumentException { } 

如果子类抛出的是一个更高级别的异常:

  1. public class Dog extends Animal { 
  2.    public void eat() throws Exception { } 

编译器同样会提示错误,因为 Exception 是 IOException 的父类。

  1. Error:(9, 16) java: com.itwanger.overriding.Dog中的eat()无法覆盖com.itwanger.overriding.Animal中的eat() 
  2.   被覆盖的方法未抛出java.lang.Exception 

05、如何调用被重写的方法?

规则七:可以在子类中通过 super 关键字来调用父类中被重写的方法。

子类继承父类的方法而不是重新实现是很常见的一种做法,在这种情况下,可以按照下面的形式调用父类的方法:

  1. super.overriddenMethodName(); 

来看例子。

  1. public class Animal { 
  2.     protected void eat() { } 

子类重写了 eat() 方法,然后在子类的 eat() 方法中,可以在方法体的第一行通过 super.eat() 调用父类的方法,然后再增加属于自己的代码。

  1. public class Dog extends Animal { 
  2.    public void eat() { 
  3.        super.eat(); 
  4.        // Dog-eat 
  5.    } 

06、重写和构造方法

规则八:构造方法不能被重写。

因为构造方法很特殊,而且子类的构造方法不能和父类的构造方法同名(类名不同),所以构造方法和重写之间没有任何关系。

07、重写和抽象方法

规则九:如果一个类继承了抽象类,抽象类中的抽象方法必须在子类中被重写。

先来看这样一个接口类:

  1. public interface Animal { 
  2.     void move(); 

接口中的方法默认都是抽象方法,通过反编译是可以看得到的:

  1. public interface Animal 
  2.     public abstract void move(); 

如果一个抽象类实现了 Animal 接口,move() 方法不是必须被重写的:

  1. public abstract class AbstractDog implements Animal { 
  2.     protected abstract void bark(); 

但如果一个类继承了抽象类 AbstractDog,那么 Animal 接口中的 move() 方法和抽象类 AbstractDog 中的抽象方法 bark() 都必须被重写:

  1. public class BullDog extends AbstractDog { 
  2.   
  3.     public void move() {} 
  4.   
  5.     protected void bark() {} 

08、重写和 synchronized 方法

规则十:synchronized 关键字对重写规则没有任何影响。

synchronized 关键字用于在多线程环境中获取和释放监听对象,因此它对重写规则没有任何影响,这就意味着 synchronized 方法可以去重写一个非同步方法。

09、重写和 strictfp 方法

规则十一:strictfp 关键字对重写规则没有任何影响。

如果你想让浮点运算更加精确,而且不会因为硬件平台的不同导致执行的结果不一致的话,可以在方法上添加 strictfp 关键字。因此 strictfp 关键和重写规则无关。

本文转载自微信公众号「沉默王二」,可以通过以下二维码关注。转载本文请联系沉默王二公众号。

 

责任编辑:武晓燕 来源: 沉默王二
相关推荐

2020-07-29 07:52:25

编程开发IT

2021-04-12 14:30:30

GitLinux命令

2023-09-01 10:43:22

IT外包企业

2021-03-19 10:11:06

VimLinux命令

2024-09-30 16:25:40

2011-03-31 09:22:56

c++

2011-05-16 13:44:11

C++

2019-07-09 13:42:12

数据备份云计算系统

2011-03-24 12:32:15

数据库性能优化

2021-08-31 11:47:20

网络工程师软件网络

2015-11-06 09:41:03

图标可视化

2023-11-04 12:08:40

Flutter事件

2011-08-29 16:05:07

高性能SQL语句SQL Server

2016-09-25 16:02:09

《硅谷》编程虚拟内存

2020-10-15 07:51:57

缓存数据库

2017-12-11 17:59:08

物联网潜能AI

2012-11-13 16:31:39

效率工作效率项目

2023-11-06 15:04:07

Flutter开发技巧

2019-09-30 08:00:00

图数据库数据库

2015-08-26 14:07:02

创业关于创业
点赞
收藏

51CTO技术栈公众号