IT七剑客 IT七剑客
首页
wresource
郭霖
孤寒者
IT邦德
沉默王二
老麦
stackoverflow
GitHub (opens new window)
首页
wresource
郭霖
孤寒者
IT邦德
沉默王二
老麦
stackoverflow
GitHub (opens new window)
  • Java基础语法

  • 程序人生

  • 实用工具

  • Java重要知识点

    • Java中常用的48个关键字和2个保留字
    • 深入理解Java中的注解
    • 深入剖析Java中的拆箱和装箱
    • 先有Class还是先有Object?
    • 详解Java中Comparable和Comparator的区别
    • 彻底讲明白的Java浅拷贝与深拷贝
    • Java枚举(enum)
    • 一次性搞清楚equals和hashCode
    • 大白话说Java反射:入门、使用、原理
    • 深入理解Java泛型
    • 深入理解Java中的hashCode方法
    • 深入理解Java中的不可变对象
    • instanceof关键字是如何实现的?
    • Java中int、Integer、new Integer之间的区别
    • Java命名规范,告别编码 5 分钟,命名 2 小时
    • 彻底弄懂Java中的Unicode和UTF-8编码
    • jdk9为何要将String的底层实现由char[]改成了byte[]?
    • 为什么JDK源码中,无限循环大多使用for(;;)而不是while(true)?
    • Java 方法重写 Override 和方法重载 Overload 的区别,一下子就明白了
    • Java重写(Overriding)时应当遵守的11条规则
      • Java到底是值传递还是引用传递?
      • Java不能实现真正泛型的原因是什么?
      • Java中可变参数的使用
    • Java工具

    • 数组与字符串

    • 沉默王二 Java
    • Java重要知识点
    沉默王二
    2023-01-22
    目录

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

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

    重写(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(){ }
    }
    
    1
    2
    3
    4
    5
    6
    7

    Dog 类来重写这三个方法:

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

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

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

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

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

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

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

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

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

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

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

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

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

    # 04、重写方法的要求

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

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

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

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

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

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

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

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

    父类没有返回类型:

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

    子类尝试返回 String:

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

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

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

    可以这样来理解:

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

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

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

    子类中的方法可以是 public:

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

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

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

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

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

    父类抛出 IOException:

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

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

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

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

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

    那编译器就会提示错误:

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

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

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

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

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

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

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

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

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

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

    super.overriddenMethodName();
    
    1

    来看例子。

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

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

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

    # 06、重写和构造方法

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

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

    # 07、重写和抽象方法

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

    先来看这样一个接口类:

    public interface Animal {
        void move();
    }
    
    1
    2
    3

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

    public interface Animal
    {
        public abstract void move();
    }
    
    1
    2
    3
    4

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

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

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

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

    # 08、重写和 synchronized 方法

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

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

    # 09、重写和 strictfp 方法

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

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


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

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

    编辑 (opens new window)
    #沉默王二#Java
    上次更新: 2023/01/22, 15:00:47
    Java 方法重写 Override 和方法重载 Overload 的区别,一下子就明白了
    Java到底是值传递还是引用传递?

    ← Java 方法重写 Override 和方法重载 Overload 的区别,一下子就明白了 Java到底是值传递还是引用传递?→

    最近更新
    01
    Coding 102 Writing code other people can read
    02-26
    02
    Kotlin Flow响应式编程,StateFlow和SharedFlow
    02-05
    03
    Kotlin Flow响应式编程,操作符函数进阶
    02-05
    更多文章>
    Theme by Vdoing | Copyright © 2022-2023 IT七剑客 | MIT License
  • 闽ICP备2021006579号-4
  • 闽公网安备 35012102500470号
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式