Java 重写时应当遵守的 11 条规则

CSDN 的小伙伴们,大家好,我是沉默王二。

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

01、什么是重写?

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

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

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

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

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

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

public class Animal {
  public void move() { }

  protected void eat() { }

  void sleep(){ }
}

Dog 类来重写这三个方法:

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

  protected void eat() { }

  void sleep(){ }
}

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

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

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

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

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

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

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

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

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

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

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

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

04、重写方法的要求

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

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

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

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

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

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

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

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

父类没有返回类型:

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

子类尝试返回 String:

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

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

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

可以这样来理解:

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

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

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

子类中的方法可以是 public:

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

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

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

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

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

父类抛出 IOException:

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

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

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

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

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 异常,那就没有冲突:

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

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

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 关键字来调用父类中被重写的方法

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

super.overriddenMethodName();

来看例子。

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

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

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

06、重写和构造方法

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

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

07、重写和抽象方法

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

先来看这样一个接口类:

public interface Animal {
  void move();
}

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

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

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

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

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

public class BullDog extends AbstractDog {

  public void move() {}

  protected void bark() {}
}

08、重写和 synchronized 方法

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

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

09、重写和 strictfp 方法

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

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

到此这篇关于Java 重写时应当遵守的 11 条规则的文章就介绍到这了,更多相关Java 重写规则内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • java中为何重写equals时必须重写hashCode方法详解

    前言 大家都知道,equals和hashcode是java.lang.Object类的两个重要的方法,在实际应用中常常需要重写这两个方法,但至于为什么重写这两个方法很多人都搞不明白. 在上一篇博文Java中equals和==的区别中介绍了Object类的equals方法,并且也介绍了我们可在重写equals方法,本章我们来说一下为什么重写equals方法的时候也要重写hashCode方法. 先让我们来看看Object类源码 /** * Returns a hash code value for

  • Java重写与重载之间的区别

    重写(Override) 重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变.即外壳不变,核心重写! 重写的好处在于子类可以根据需要,定义特定于自己的行为. 也就是说子类能够根据需要实现父类的方法. 重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常.例如: 父类的一个方法申明了一个检查异常 IOException,但是在重写这个方法的时候不能抛出 Exception 异常,因为 Exception 是 IOException 的父类,只能抛出 IOEx

  • java子类调用父类的方法中包含子类重写的实例方法

    # 看题目是不是很绕,这个我也不知道怎么才能更简单的表达了 # 先看代码: public class Common { public static void main(String[] args) { Sub sub = new Sub(); sub.testSub(); } } class Parent { protected boolean test() { throw new RuntimeException(); } protected void testParent() { if (t

  • Java 重写与重载方法与区别详解

    重写(Override) 重写是子类对父类的允许访问的方法的实现过程进行重新编写!返回值和形参都不能改变.即外壳不变,核心重写! 重写的好处在于子类可以根据需要,定义特定于自己的行为. 也就是说子类能够根据需要实现父类的方法. 在面向对象原则里,重写意味着可以重写任何现有方法.实例如下: class Animal{ public void move(){ System.out.println("动物可以移动"); } } class Dog extends Animal{ public

  • Java 重载、重写、构造函数的实例详解

    Java 重载.重写.构造函数的实例详解 方法重写 1.重写只能出现在继承关系之中.当一个类继承它的父类方法时,都有机会重写该父类的方法.一个特例是父类的方法被标识为final.重写的主要优点是能够定义某个子类型特有的行为. class Animal { public void eat(){ System.out.println ("Animal is eating."); } } class Horse extends Animal{ public void eat(){ Syste

  • java编程枚举类型那些事!枚举类型定义和重写枚举的方法

    什么是枚举类型 枚举类型(Enumerated Type) 很早就出现在编程语言中,它被用来将一组类似的值包含到一种类型当中. 而这种枚举类型的名称则会被定义成独一无二的类型描述符,在这一点上和常量的定义相似.不过相比较常量类型,枚举类型可以为申明的变量提供更大的取值范围. 简单的枚举类型定义 假如你希望创建一个颜色库,你可以这样定义枚举类型 enum Color {red, green, yellow, black}; 然后你就可以使用它 Color T = Color.red; if (T

  • Java 重写时应当遵守的 11 条规则

    CSDN 的小伙伴们,大家好,我是沉默王二. 重写(Overriding)算是 Java 中一个非常重要的概念,理解重写到底是什么对每个 Java 程序员来说都至关重要,这篇文章就来给大家说说重写过程中应当遵守的 12 条规则. 01.什么是重写? 重写带来了一种非常重要的能力,可以让子类重新实现从超类那继承过来的方法.在下面这幅图中,Animal 是父类,Dog 是子类,Dog 重新实现了 move() 方法用来和父类进行区分,毕竟狗狗跑起来还是比较有特色的. 重写的方法和被重写的方法,不仅方

  • Java程序员应该遵守的10条纪律

    有哪些"纪律"是Java程序员所要遵守的? 1. 为代码添加注释(Add comments to your code). – 每个人都知道这一点,但不是每个人都会这么做.你有多少次"忘记"添加注释了?确实,注释不会为你的程序增加任何函数功能.但是,有多少次,看到2周前写的代码,你都记不起它是干什么的?你很幸运,那些未注释的代码是你自己写的,你脑海中还会有残存的印象.非常不幸,大多时候,代码是别人写的,并且那个人很可能已经离开公司了.有句谚语说的好:"有来有

  • java方法重写时需要注意的问题

    目录 注意事项1:子类权限控制符不能变小 注意事项2:子类返回值类型只能变小 注意事项3:抛出的异常类型只能变小 注意事项4:方法名必须保持一致 注意事项5:方法的参数类型和个数必须保持一致 总结 在面向对象编程中,方法重写(override)是一种语言特性,它是多态的具体表现,它允许子类重新定义父类中已有的方法,且子类中的方法名和参数类型及个数都必须与父类保持一致,这就是方法重写. 方法重写最简单的示例如下,定义一个父类 Father 和子类 Son,父类中有一个 method 方法,而在子类

  • Java中 equals 重写时为什么一定也要重写 hashCode

    目录 1.equals 方法 2.hashCode 方法 2.1 hashCode 使用 3.为什么要一起重写? 3.1 Set 正常使用 3.2 Set 集合的“异常” 3.3 解决“异常” 3.4 原因分析 总结 前言: equals 方法和 hashCode 方法是 Object 类中的两个基础方法,它们共同协作来判断两个对象是否相等.为什么要这样设计嘞?原因就出在“性能” 2 字上. 使用过 HashMap 我们就知道,通过 hash 计算之后,我们就可以直接定位出某个值存储的位置了,那

  • 浅谈java 重写equals方法的种种坑

    重写java object类的equals方法 覆盖equals方法请遵守约定 什么情况下要覆盖equals方法 容易违反的对称性 不易察觉的传递性 覆盖equals请遵守通用约定 似乎覆盖equals方法看起来似乎是一件平常甚至极其简单的事情, 但是有许多覆盖方式会导致错误,并且会表现出超出预期的行为, 而有可能数小时也无法找到错误的位置.(比如说把参数改成了非Object类型) 1. 类的每一个实例在本质上都是唯一的 ( 从内存的角度来讲是这样的),对于代表活动而不是值(value)的类来说

  • 【经验总结】编写JavaScript代码时应遵循的14条规律

    本文讲述了编写JavaScript代码时应遵循的14条规律.分享给大家供大家参考,具体如下: 1. 总是使用 'var' 在javascript中,变量不是全局范围的就是函数范围的,使用"var"关键词将是保持变量简洁明了的关键.当声明一个或者是全局或者是函数级(function-level)的变量,需总是前置"var"关键词,下面的例子将强调不这样做潜在的问题. 不使用 Var 造成的问题 var i=0; // This is good - creates a

  • Java运行时数据区概述详解

    Java 虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些区域都有各自的用途,如图所示: 程序计数器 程序计数器是一块比较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器. 在虚拟机的概念模型中(仅是概念模型,各种虚拟机可能会通过一些更加高效的方式去实现),字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支.循环.跳转.异常处理.线程恢复等基础功能都需要依赖这个计数器来完成. 如果线程正在执行一个Java方法,则这个计数

  • Java重写(Override)与重载(Overload)区别原理解析

    这篇文章主要介绍了Java重写(Override)与重载(Overload)区别原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 重写(Override) 重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变.即外壳不变,核心重写! 重写的好处在于子类可以根据需要,定义特定于自己的行为. 也就是说子类能够根据需要实现父类的方法. 重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常.例如: 父类的一个

  • Java运行时动态生成类实现过程详解

    最近一个项目中利用规则引擎,提供用户拖拽式的灵活定义规则.这就要求根据数据库数据动态生成对象处理特定规则的逻辑.如果手写不仅每次都要修改代码,还要每次测试发版,而且无法灵活根据用户定义的规则动态处理逻辑.所以想到将公共逻辑写到父类实现,将特定逻辑根据字符串动态生成子类处理.这就可以一劳永逸解决这个问题. 那就着手从Java如何根据字符串模板在运行时动态生成对象. Java是一门静态语言,通常,我们需要的class在编译的时候就已经生成了,为什么有时候我们还想在运行时动态生成class呢? 经过一

  • java运行时数据区域和类结构详解

    Java运行时数据区域 java运行时数据区可以分为:方法区.虚拟机栈.本地方法栈.堆和程序计数器 线程私有:虚拟机栈.本地方法栈.程序计数器 线程共享:方法区.堆 程序计数器 一块较小的内存空间,当前线程所执行字节码的行号指示器,它是程序控制流的指示器,分支.循环.跳转.异常处理.线程恢复等基础功能都需要依赖这个计数器来完成. 每条线程都拥有一个独立的程序计数器. Java虚拟机栈 线程私有的,它的生命周期与线程相同. 每个方法被执行时,java虚拟机都会创建一个栈帧,用于存储 局部变量表.操

随机推荐