一文带你搞懂Java中方法重写与方法重载的区别
作者:一一哥Sun 发布时间:2022-05-14 03:19:13
一. 方法重写
在面向对象中,实现多态的必备条件是继承、重写和向上转型,现在我们已经学习了什么是继承。接下来我们再来学习什么是方法重写,这是我们能够实现多态的前提。
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注解;
方法签名要相同;
返回值类型一致;
访问修饰符要更宽泛;
声明的异常类型要一致。
三. 结语
现在你知道方法重写是怎么回事了吗?另外方法重载和方法重写的区别,是我们面试初级程序员时很常见的题目,大家一定要牢牢掌握哦。现在有了方法重写的基础,接下来我们就可以学习多态的内容了,敬请关注下一篇文章哦。
来源:https://juejin.cn/post/7215736946252283964


猜你喜欢
- Java-JDK * (AOP)使用及实现原理分析第一章:代理的介绍介绍:我们需要掌握的程度 * (理解) 基于反射机制掌握的程度:1.
- 关于[Cannot determine value type from string ‘xxx’]的
- 使用redis scan方法无法获取connection,导致线程锁死。0、关键字redisspringbootredistemplates
- Form1主窗体:public delegate void SetVisiableHandler();//定义委托类型private voi
- 一个完整的http响应包括响应行,若干响应头和响应数据主体三部分构成。如果我们能用响应对象来进行这三部分的处理,就能向客户发送特定的响应数据
- 使用spring框架实现数据库事务处理事务对于数据库来说是,是对sql语句的一系列操作,这些操作被组织成为一个事务。事务具有原子性的,要么全
- 在开发中,用到springboot项目,当打包后部署运行时,出现了这个问题,网上搜了好多,又是加META-INF配置,又是加啥的,感觉spr
- 开发一个需要常住后台的App其实是一件非常头疼的事情,不仅要应对国内各大厂商的ROM,还需要应对各类的安全管家...虽然不断的研究各式各样的
- 首先分析下游戏界面内的元素:无限滚动的背景图, 可以操作的主角,主角的 * , 主角的血量,两种怪物(敌机),一个boss, boss的 * 效
- 一. Window 分类应用 Window(ApplicationWindow: 对应一个 Acitivity)
- 目录什么是角点?为什么要检测角点?Harris角点检测API操作效果源码什么是角点?角点就是极值点,即在某方面属性特别突出的点。当然,你可以
- 如果需要实现跨服务器上传文件,就是将我们本地的文件上传到资源服务器上,比较好的办法就是通过ftp上传。这里是结合SpringMVC+ftp的
- 什么是构建生命周期构建生命周期是一组阶段的序列(sequence of phases),这些构建生命周期中的每一个由构建阶段的不同列表定义,
- Gizmos是场景视图里的一个可视化调试工具。在做项目过程中。我们常常会用到它,比如:绘制一条射线等。Unity3D 4.2版本号截至。眼下
- Java-关键字:final1 .final可以用来修饰的结构:类、方法、变量2.final 用来修饰一个类:此类不能被其他类所继承比如:S
- 使用异步包(推荐)async包由 Dart 编程语言的作者开发和发布。它提供了dart:async风格的实用程序来增强异步计算。可以帮助我们
- protected bool IsChineseLetter(string input,int index){int code = 0;in
- AlertDialog可以在当前的界面上显示一个对话框,这个对话框是置顶于所有界面元素之上的,能够屏蔽掉其他控件的交互能力,因此AlertD
- Map 中ConcurrentHashMap是线程安全的,但不是所有操作都是,例如get()之后再put()就不是了,这时使用merge()
- 实例如下所示:public class WebServiceHelper { /// <summary>