一文带你搞懂Java中方法重写与方法重载的区别

目录
  • 一. 方法重写
    • 1. 概念
    • 2. 基本要求
    • 3. 注意事项
    • 4. 代码实现
      • 4.1 定义父类
      • 4.2 定义子类
      • 4.3 @Override注解
    • 5. 变量隐藏
      • 5.1 概念
      • 5.2 案例实现
    • 6. 方法隐藏
      • 6.1 概念
      • 6.2 案例实现
      • 6.3 小结
  • 二. 重写与重载的区别
    • 1. 重载的特点
    • 2. 重写的特点
  • 三. 结语

一. 方法重写

在面向对象中,实现多态的必备条件是继承、重写和向上转型,现在我们已经学习了什么是继承。接下来我们再来学习什么是方法重写,这是我们能够实现多态的前提。

1. 概念

如果我们在子类中,创建了一个与父类中名称、返回值类型、参数列表都完全相同的方法,只是方法体的功能实现不同,这种方式被称为方法重写(override) ,或者叫方法覆盖。当父类中的方法无法满足子类的需求,或者子类需要有特殊功能时,就可以进行方法重写。

2. 基本要求

我们在进行方法重写时,需要遵循以下几点要求:

  • 父类的成员方法只能被它的子类重写,即不能继承一个方法,就不能重写这个方法;
  • 被final修饰的方法不能被重写;
  • 被static修饰的方法不能被重写,但可以再次声明;
  • 构造方法不能被重写;
  • 子类和父类在同一个包中时,子类可以重写父类中除了被private和final修饰的其他所有方法;
  • 子类和父类不在同一个包中时,子类只能重写父类被public和protected修饰的非final方法;
  • 重写的方法建议使用@Override注解来标识。

3. 注意事项

另外我们在进行方法重写时,还要注意以下几点:

  • 方法签名要相同:重写的方法和被重写的方法,在方法名、参数上都要相同;
  • 返回值类型一致:JDK 1.5之前重写方法的返回值类型必须一样,但之后的Java版本放宽了限制,返回值类型必须小于或等于父类方法的返回值类型
  • 访问修饰符要更宽泛:子类重写父类的方法时,子类方法中的访问修饰符不能比父类中的更严格(public>protected>default>private)。比如父类方法的修饰符是protected,则子类的同名方法其修饰符可以是protected或public,但不能是默认的或private;
  • 声明的异常类型要一致:重写的方法一定不能抛出新的检査异常,或者比被重写方法声明更宽泛的检査型异常。例如,父类的方法声明了IOException,重写该方法时就不能抛出Exception,只能拋出IOException或其子类异常。但可以抛出非检査异常

4. 代码实现

接下来就通过一个案例来给大家讲解方法的重写该怎么实现。

4.1 定义父类

我们先定义一个Father父类,要注意父类中有哪些方法不能被重写。

public class Father {
    // 父类中的成员变量--变量隐藏
    String name="老子";

    //构造方法不能被重写,因为构造方法不能被继承!
    public Father() {
	System.out.println("爹的构造方法");
    }

    // 吃
    public void eat() {
	System.out.println("爹吃馒头");
    }

    // 喝
    public void drink() {
	System.out.println("爹喝水");
    }

    // 玩
    //私有方法不能被重写
    //	private void play() {
    //       System.out.println("爹玩火");
    //	}

    //静态方法不能被重写,但可以在子类中声明一个同样的静态方法。
    //	public static void play() {
    //      System.out.println("爹玩火");
    //	}

    //final方法不能被重写
    public final void play() {
	System.out.println("爹玩火");
    }
}

4.2 定义子类

定义一个Son子类继承父类,有了继承才会有重写!

public class Son extends Father{
    //构造方法不能被重写,因为构造方法不能被继承!
    //@Override
    //public Father() {}

    // 吃
    /**
    * 方法重写时可以带有@Ovriride关键词,当重写的方法签名不一致时,可以有编译错误的提示。
    * 否则方法签名不一致时不会有错误提示,会被当做一个新的方法来处理。
    */
    @Override
    public void eat() {
	//如果子类的功能,是在父类的基础之上进行的额外扩展增加,
	//我们可以使用super关键字调用父类的同名方法,然后再进行自己的额外扩展!
	//如果子类的实现和父类完全不一样,可以不调用super!
	super.eat();
	//方法重写时,子类可以对父类的同名方法进行扩展实现,方法体的内容可以和父类中的实现不一样
	System.out.println("儿子吃肉");
    }

    // 喝
    @Override
    public void drink() {
	//如果子类的实现和父类完全不一样,可以不调用super!
	System.out.println("儿子喝酒");
    }
    /**
    * 父类私有的、static、final等方法无法被重写
    */
    //@Override
    //public void play() {}

    //static静态的父类方法不能被重写,但可以在子类中再重新编写一个静态的同名方法。
    //public static void play() {}

    //变量隐藏--调用父类和子类中的同名成员变量
    public void sayHello() {
	// 如果子类的实现和父类完全不一样,可以不调用super!
	System.out.println("父亲的名字=" + super.name);
	System.out.println("儿子的名字=" + name);
    }

    public static void main(String[] args) {
	Son son = new Son();
	son.sayHello();
    }
}

我们在进行方法重写时,要注意以下几点:

  • 方法重写时可以带有@Ovriride关键词。当重写的方法签名不一致时,会有编译错误的提示,否则方法签名不一致时不会有错误提示,会被当做一个新的方法来处理。
  • 当子类对象调用重写的方法时,默认执行的是子类的方法,而不是父类中被重写的方法。如果我们想要调用父类中被重写的方法,则可以使用“super.方法名”的形式。
  • 如果子类的功能是在父类的基础之上进行的额外扩展,我们可以使用super关键字调用父类的同名方法,然后再进行自己的额外扩展!
  • 如果子类的实现和父类完全不一样,可以不调用super!
  • 方法重写时,子类可以对父类的同名方法进行扩展实现,方法体的内容可以和父类中的实现不一样。

4.3 @Override注解

在上面的代码中,我们用到了一个新的关键字@Override。在Java中,@Override是一个注解,关于注解的更多内容,会在后面的文章中进行专门讲解,现在我们先知道注解这个概念就行。

@Override是一个用来修饰被重新的方法的注解,只能用在被重新的方法上,不能用在其它的地方。该注解可以强制子类必须重写父类的方法或者接口中的方法,主要是告诉编译器检查重写的方法是否和父类中定义的一致。如果重写的方法签名不一致,会提示编译错误。如果方法签名不一致,则不会有错误提示,会被当做一个新的方法来处理。通过这样的机制,就可以避免程序员出现一些低级的错误。

5. 变量隐藏

5.1 概念

如果子类中定义了一个成员变量,而该变量的名称与父类中的成员变量相同,数据类型不一定完全一致,我们就把这称为变量隐藏。也就是说,子类的成员变量,对从父类继承过来的成员变量进行了重新定义,出现了子类变量对父类变量的隐藏。所以子类执行自己定义的方法时,操作的成员变量默认是自己定义的变量,而不是父类中的同名变量。如果我们非要操作隐藏的成员变量,可以使用super关键字进行调用。

接下来我们通过一个案例来给大家演示变量隐藏的使用。

5.2 案例实现

父类中定义一个成员变量name,如下图所示:

子类中也定义一个相同的成员变量name,如下图所示:

如果我们在Son类中直接使用name,默认使用的是Son自己的变量;如果我们想使用Father类中的name变量,则可以通过“super.属性”的形式进行。执行结果如下图所示:

6. 方法隐藏

在子类继承父类时,既然存在变量隐藏的现象,同理也存在方法隐藏的现象。

6.1 概念

我们知道,方法的重写是子类覆盖父类的对象方法,而方法隐藏则是子类覆盖父类的 静态方法(类方法) 。在java中的静态方法能被子类继承吗?答案是肯定的,但若子类中有与父类中同名同参的方法,则父类的方法将被隐藏。

6.2 案例实现

我们先定义一个Father父类,里面有个静态方法eat。

/**
 * @author 一一哥Sun
 *
 * 定义父类
 */
public class Father {
    // 吃---静态方法
    public static void eat() {
	System.out.println("爹吃馒头");
    }
}

然后再定义一个Son子类,里面也有一个静态方法eat。我们知道,静态方法是可以被继承的,所以如果Son子类中没有定义自己的eat()方法,默认可以使用Father父类中的eat()方法。但如果我们在子类中也定义了一个eat()方法,子类的同名静态方法就会隐藏父类中的eat()方法,这就是方法隐藏。

/**
 * @author 一一哥Sun
 *
 * 子类继承父类
 */
public class Son extends Father {
    // 吃---静态方法
    //如果子类中没有定义该方法,则子类可以继承使用父类的eat()方法
    public static void eat() {
	//子类覆盖父类中的同名静态方法(类)
	System.out.println("儿子吃肉");
    }

    public static void main(String[] args) {
	//调用子类自己的静态方法
	eat();

	//调用父类的静态方法
	Father.eat();
    }
}

执行结果如下图所示:

6.3 小结

通过本案例,我们可以得出以下结论:

子类可以继承父类中的静态方法;

子类无法重写父类中的静态方法,但可以重载;

若子类中定义了与父类中同样的静态方法,则父类的同名方法会被子类隐藏

二. 重写与重载的区别

在Java中既有方法重写(Override),也有方法重载(Overload),对于初学者来说很容易搞混。所以有不少面试官,在招聘初级程序员时,就很喜欢问我们方法重写与方法重载的区别。

其实方法重写Override和方法重载Overload的最大不同,在于方法签名的不同。如果同一个类中的多个方法签名不同,就是方法重载Overload,重载出的方法是一个新方法。如果父子类之间的多个方法签名相同,且返回值也相同,就是方法重写Override。

当然,如果你想把关于重写和重载的区别说得更详细,可以参考以下章节。

1. 重载的特点

  • 方法重载要求方法同名不同参(参数类型、个数、顺序);
  • 重载的方法与返回值、访问修饰符无关;
  • 重载的方法发生在同一个类中,是在一个类中创建多个同名的方法。

2. 重写的特点

  • 重写的方法发生在父子类中,需要有继承关系;
  • 父类的成员方法只能被它的子类重写,即不能继承一个方法,就不能重写这个方法;
  • 被final修饰的方法不能被重写;
  • 被static修饰的方法不能被重写,但可以再次声明;
  • 构造方法不能被重写;
  • 子类和父类在同一个包中时,子类可以重写父类中除了被private和final修饰的其他所有方法;
  • 子类和父类不在同一个包中时,子类只能重写父类被public和protected修饰的非final方法;
  • 方法重写时可以使用@Override注解;
  • 方法签名要相同
  • 返回值类型一致
  • 访问修饰符要更宽泛
  • 声明的异常类型要一致

三. 结语

现在你知道方法重写是怎么回事了吗?另外方法重载和方法重写的区别,是我们面试初级程序员时很常见的题目,大家一定要牢牢掌握哦。现在有了方法重写的基础,接下来我们就可以学习多态的内容了,敬请关注下一篇文章哦。

以上就是一文带你搞懂Java中方法重写与方法重载的区别的详细内容,更多关于Java 方法重写与方法重载的区别的资料请关注我们其它相关文章!

(0)

相关推荐

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

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

  • Java方法重载和方法重写的区别到底在哪?

    方法重载和方法重写的区别 方法重载 方法重载的主要是在一个类当中,方法的方法名相同,参数列表不同,返回值类型可以相同,也可以不同. /* 这里只是简单的示例一下,Food Snack没有给出,意会一下即可. */ public class Demo{ public void eat(Food food){ System.out.println("今天正常吃饭!吃了" + food.name); } public void eat(Snack snack){ System.out.pri

  • 详解Java中方法重写和方法重载的6个区别

    目录 1.方法重写 1.1 基本用法 1.2 使用场景 1.3 注意事项 2.方法重载 2.1 基本使用 2.2 使用场景 2.3 注意事项 3.方法重写 VS 方法重载 总结 方法重写(Override)和方法重载(Overload)都是面向对象编程中,多态特性的不同体现,但二者本身并无关联,它们的区别犹如马德华之于刘德华的区别,除了名字长得像之外,其他的都不像. 接下来咱们就来扒一下二者的具体区别. 1.方法重写 方法重写(Override)是一种语言特性,它是多态的具体表现,它允许子类重新

  • java方法重载和参数类型自动提升方式

    目录 方法重载和参数类型自动提升 方法重载的意思是 对于基本类型数据而言 java基本类型重载 1.重载 2.基本类型重载 3.代码示例 4.重载解析过程 5.可行集获取的原则 方法重载和参数类型自动提升 方法重载的意思是 一个类中可以由多个方法具有相同的名字,但这些方法的参数必须不同, 参数不同是指满足下列之一: 参数的个数不同 参数的个数相同,但参数列表中对相应的某个参数的类型不同 对于基本类型数据而言 参数类型存在自动提升 以byte a = 1为例,存在如下自动提升方式: byte–>

  • java方法重载示例

    什么是方法的重载? 方法重载是以统一的方式处理不同数据类型的一种手段. 怎样构成方法的重载? 方法名相同, 形参不同.而形参的不同又表示在:  1). 形参的个数不同  2). 形参的类型不同 3). 形参的顺序不同 注意事项 1. 如果两个方法的返回值不同, 而其他都相同. 这个时候并不构成方法的重载. 在编译的时候会报错: 示例代码(错误):Test.java 复制代码 代码如下: /*返回值的不同并不能构成方法的重载*/public class Test {    public stati

  • 一文解析Java中的方法重写

    目录 1.含义 2.为什么要使用方法重写 3.如何使用方法重写 3.1 基本语法 3.2 具体分析 3.3 方法重写的一些小技巧 1.含义 子类继承父类后,可以在子类中书写一个与父类同名同参的方法,从而实现对父类中同名同参数的方法的覆盖,我们把这一过程叫做方法的重写(override) 2.为什么要使用方法重写 2.1 当父类的方法满足不了子类的需求的时候,需要在子类中对该方法进行重写 2.2 题目与分析 例如存在一个父类Peple,子类Chinese,父类中有一个say()方法,输出人在说话,

  • 一文带你搞懂Java中的泛型和通配符

    目录 概述 泛型介绍和使用 泛型类 泛型方法 类型变量的限定 通配符使用 无边界通配符 通配符上界 通配符下界 概述 泛型机制在项目中一直都在使用,比如在集合中ArrayList<String, String>, Map<String,String>等,不仅如此,很多源码中都用到了泛型机制,所以深入学习了解泛型相关机制对于源码阅读以及自己代码编写有很大的帮助.但是里面很多的机制和特性一直没有明白,特别是通配符这块,对于通配符上界.下界每次用每次百度,经常忘记,这次我就做一个总结,加

  • 一文带你搞懂Java中Get和Post的使用

    目录 1 Get请求数据 1.1 Controller 1.2 Service 1.3 Application 1.4 Postman 2 Post接收数据 2.1 Controller 2.2 Service 2.3 Application 2.4 Postman 3 Post发送数据 3.1 Controller 3.2 Service 3.3 ResponseResult 3.4 Config 3.5 Application 3.6 Postman 1 Get请求数据 项目地址:https

  • 一文带你搞懂Java中的递归

    目录 概述 递归累加求和 计算1 ~ n的和 代码执行图解 递归求阶乘 递归打印多级目录 综合案例 文件搜索 文件过滤器优化 Lambda优化 概述 递归:指在当前方法内调用自己的这种现象. 递归的分类: 递归分为两种,直接递归和间接递归. 直接递归称为方法自身调用自己. 间接递归可以A方法调用B方法,B方法调用C方法,C方法调用A方法. 注意事项: 递归一定要有条件限定,保证递归能够停止下来,否则会发生栈内存溢出. 在递归中虽然有限定条件,但是递归次数不能太多.否则也会发生栈内存溢出. 构造方

  • 一文带你搞懂Java中Object类和抽象类

    目录 一.抽象类是什么 二.初始抽象类 2.1 基本语法 2.2 继承抽象类 三.抽象类总结 四.Object类 4.1 初始Object 4.2 toString 4.3 equals 4.4 hashcode 一.抽象类是什么 在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类. 由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用.也是因为这个原因,通常在设计阶段决定要

  • 一文带你搞懂Java中Synchronized和Lock的原理与使用

    目录 1.Synchronized与Lock对比 2.Synchronized与Lock原理 2.1 Synchronized原理 2.2 Lock原理 3.Synchronized与Lock使用 Synchronized Lock 4.相关问题 1.Synchronized与Lock对比 实现方式:Synchronized是Java语言内置的关键字,而Lock是一个Java接口. 锁的获取和释放:Synchronized是隐式获取和释放锁,由Java虚拟机自动完成:而Lock需要显式地调用lo

  • 一文带你搞懂JS中六种For循环的使用

    目录 一.各个 for 介绍 1.for 2.for ... in 3.for ... of 4.for await...of 5.forEach 6.map 二.多个 for 之间区别 1.使用场景差异 2.功能差异 3.性能差异 三.for 的使用 for 循环在平时开发中使用频率最高的,前后端数据交互时,常见的数据类型就是数组和对象,处理对象和数组时经常使用到 for 遍历,因此下班前花费几分钟彻底搞懂这 5 种 for 循环.它们分别为: for for ... in for ... o

  • 一文带你搞懂Numpy中的深拷贝和浅拷贝

    目录 1. 引言 2. 浅拷贝 2.1 问题引入 2.2 问题剖析 3. 深拷贝 3.1 举个栗子 3.2 探究原因 4. 技巧总结 4.1 判断是否指向同一内存 4.2 其他数据类型 5. 总结 1. 引言 深拷贝和浅拷贝是Python中重要的概念,本文重点介绍在NumPy中深拷贝和浅拷贝相关操作的定义和背后的原理. 闲话少说,我们直接开始吧! 2. 浅拷贝 2.1 问题引入 我们来举个栗子,如下所示我们有两个数组a和b,样例代码如下: import numpy as np a = np.ar

  • 一文带你搞懂Python中的文件操作

    目录 一.文件的编码 二.文件的读取 2.1 open()打开函数 2.2 mode常用的三种基础访问模式 2.3 读操作相关方法 三.文件的写入 写操作快速入门 四.文件的追加 追加写入操作快速入门 五.文件操作综合案例 一.文件的编码 计算机中有许多可用编码: UTF-8 GBK Big5 等 UTF-8是目前全球通用的编码格式 除非有特殊需求,否则,一律以UTF-8格式进行文件编码即可. 二.文件的读取 2.1 open()打开函数 注意:此时的f是open函数的文件对象,对象是Pytho

  • 一文带你弄懂Java中线程池的原理

    目录 为什么要用线程池 线程池的原理 ThreadPoolExecutor提供的构造方法 ThreadPoolExecutor的策略 线程池主要的任务处理流程 ThreadPoolExecutor如何做到线程复用的 四种常见的线程池 newCachedThreadPool newFixedThreadPool newSingleThreadExecutor newScheduledThreadPool 小结 在工作中,我们经常使用线程池,但是你真的了解线程池的原理吗?同时,线程池工作原理和底层实

  • 一文带你搞懂JavaScript中的进制与进制转换

    目录 进制介绍 进制转换 parseInt(str, radix) Number() +(一元运算符) Number.prototype.toString(radix) 自定义转换 十进制与十六进制转换 十进制和二进制转换 进制介绍 JavaScript 中提供的进制表示方法有四种:十进制.二进制.十六进制.八进制. 对于数值字面量,主要使用不同的前缀来区分: 十进制(Decimal):取值数字 0-9:不用前缀. 二进制(Binary):取值数字 0 和 1 :前缀 0b 或 0B. 十六进制

随机推荐