Overriding.md 9.7 KB
Newer Older
沉默王二's avatar
沉默王二 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
---
category:
  - Java核心
tag:
  - Java
---

# Java重写(Overriding)时应当遵守的11条规则

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

### 01、什么是重写?

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

![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/basic-extra-meal/Overriding-1.png)

重写的方法和被重写的方法,不仅方法名相同,参数也相同,只不过,方法体有所不同。

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

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

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

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

```java
public class Animal {
    public void move() { }

    protected void eat() { }
    
    void sleep(){ }
}
```

Dog 类来重写这三个方法:

```java
public class Dog extends Animal {
    public void move() { }

    protected void eat() { }

    void sleep(){ }
}
```

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

```java
public class Animal {
    private void move() { }
}
```

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

```java
public class Dog extends Animal {
    public void move() { }
}
```

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

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

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

```java
public class Animal {
    final void move() { }
}
```

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

![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/basic-extra-meal/Overriding-2.png)

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

```java
public class Animal {
    final void move() { }
}
```

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

![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/basic-extra-meal/Overriding-3.png)

### 04、重写方法的要求

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

```java
public class Animal {
    void eat(String food) { }
}
```

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

```java
public class Dog extends Animal {
    public void eat(String food) { }
}
```

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

```java
public class Dog extends Animal {
    public void eat(String food, int amount) { }
}
```

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

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

父类没有返回类型:

```java
public class Animal {
    void eat(String food) { }
}
```

子类尝试返回 String:

```java
public class Dog extends Animal {
    public String eat(String food) {
        return null;
    }
}
```

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

![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/basic-extra-meal/Overriding-4.png)

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

可以这样来理解:

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

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

```java
public class Animal {
    protected void eat() { }
}
```

子类中的方法可以是 public:

```java
public class Dog extends Animal {
    public void eat() { }
}
```

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

![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/basic-extra-meal/Overriding-5.png)

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

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

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

父类抛出 IOException:

```java
public class Animal {
    protected void eat() throws IOException { }
}
```

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

```java
public class Dog extends Animal {
   public void eat() throws FileNotFoundException { }
}
```

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

```java
public class Dog extends Animal {
   public void eat() throws FileNotFoundException, InterruptedException { }
}
```

那编译器就会提示错误:

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

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

```java
public class Dog extends Animal {
   public void eat() throws FileNotFoundException, IllegalArgumentException { }
}
```

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

```java
public class Dog extends Animal {
   public void eat() throws Exception { }
}
```

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

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

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

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

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

```java
super.overriddenMethodName();
```

来看例子。

```java
public class Animal {
    protected void eat() { }
}
```

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

```java
public class Dog extends Animal {
   public void eat() {
       super.eat();
       // Dog-eat
   }
}
```

### 06、重写和构造方法

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

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

### 07、重写和抽象方法

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

先来看这样一个接口类:

```java
public interface Animal {
    void move();
}
```

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

```java
public interface Animal
{
    public abstract void move();
}
```

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

```java
public abstract class AbstractDog implements Animal {
    protected abstract void bark();
}
```

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

```java
public class BullDog extends AbstractDog {
 
    public void move() {}
 
    protected void bark() {}
}
```

### 08、重写和 synchronized 方法

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

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

### 09、重写和 strictfp 方法

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

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

----

最近整理了一份牛逼的学习资料,包括但不限于Java基础部分(JVM、Java集合框架、多线程),还囊括了 **数据库、计算机网络、算法与数据结构、设计模式、框架类Spring、Netty、微服务(Dubbo,消息队列) 网关** 等等等等……详情戳:[可以说是2022年全网最全的学习和找工作的PDF资源了](https://tobebetterjavaer.com/pdf/programmer-111.html)

关注二哥的原创公众号 **沉默王二**,回复**111** 即可免费领取。

![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/xingbiaogongzhonghao.png)