一、前言
本文内容摘自《深入理解Java核心技术:写给Java工程师的干货笔记(基础篇)》一书,2022年出版,作者 张洪亮(@Hollis),阿里巴巴技术专家,著有《Java工程师成神之路》系列文章,《Java工程师成神之路》电子书已开源,可在阿里云开发者社区免费下载。书籍内容比电子书内容要丰富,内容有修改,有需要的读者可以购买正版书籍。
【如何成神:先搬砖,再砌砖,后造砖!】
本文由 @大白有点菜 原创,请勿盗用,转载请说明出处!如果觉得文章还不错,请点点赞,加关注,谢谢!
《Java工程师成神之路》下载地址为:
https://developer.aliyun.com/ebook/395?spm=a2c6h.20345107.ebook-index.24.4c927863j10ats
二、第2章 面向对象的核心概念【重载与重写】
1、重载
重载(Overloading)
:在同一个类中,多个函数或者方法有相同的名称,但是参数列表(包括参数个数、参数类型、参数顺序)不同。
【重载例子(不是照搬书中例子,而是进行扩展)】
核心代码:
/**
* 狗类
*/
public class Dog {
/**
* 狗叫:无参
*/
public void bark() {
System.out.println("旺财 汪汪叫!");
}
/**
* 狗叫:1个参数
* @param num 狗叫次数
*/
public void bark(int num) {
for (int i = 0; i < num; i++) {
System.out.println("旺财 第" + (i+1) + "次汪汪叫!");
}
}
/**
* 狗叫:2个参数,参数类型不同
* @param dogName 狗子的名称
* @param num 狗叫次数
*/
public void bark(String dogName, int num) {
// 如果狗子没名字,那就叫“旺财”吧
if (dogName == null || dogName.equals("")) {
dogName = "旺财";
}
for (int i = 0; i < num; i++) {
System.out.println(dogName + " 第" + (i+1) + "次汪汪叫!");
}
}
/**
* 狗叫:2个参数,参数类型不同,参数顺序不同
* @param num 狗叫次数
* @param dogName 狗子的名称
*/
public void bark(int num, String dogName) {
// 如果狗子没名字,那就叫“旺财”吧
if (dogName == null || dogName.equals("")) {
dogName = "旺财";
}
for (int i = 0; i < num; i++) {
System.out.println(dogName + " 第" + (i+1) + "次汪汪叫!");
}
}
/**
* 狗叫:2个参数,参数类型相同,参数名称不相同
* @param dog1Name 狗子1的名称
* @param dog2Name 狗子2的名称
*/
public void bark(String dog1Name, String dog2Name) {
// 如果狗子1没名字,那就叫“大旺财”吧
if (dog1Name == null || dog1Name.equals("")) {
dog1Name = "大旺财";
}
// 如果狗子2没名字,那就叫“小旺财”吧
if (dog2Name == null || dog2Name.equals("")) {
dog2Name = "小旺财";
}
System.out.println(dog1Name + " 和 " + dog2Name + " 都在汪汪叫!");
}
}
主函数:
public class OverloadingApp {
public static void main(String[] args) {
Dog dog = new Dog();
System.out.println("**********我是分割线1**********");
dog.bark();
System.out.println("**********我是分割线2**********");
dog.bark(6);
System.out.println("**********我是分割线3**********");
dog.bark("汪星人1", 3);
System.out.println("**********我是分割线4**********");
dog.bark(3, "汪星人2");
System.out.println("**********我是分割线5**********");
dog.bark(null, null);
}
}
运行结果:
D:\Develop\JDK\jdk1.8.0_281\bin\java.exe
**********我是分割线1**********
旺财 汪汪叫!
**********我是分割线2**********
旺财 第1次汪汪叫!
旺财 第2次汪汪叫!
旺财 第3次汪汪叫!
旺财 第4次汪汪叫!
旺财 第5次汪汪叫!
旺财 第6次汪汪叫!
**********我是分割线3**********
汪星人1 第1次汪汪叫!
汪星人1 第2次汪汪叫!
汪星人1 第3次汪汪叫!
**********我是分割线4**********
汪星人2 第1次汪汪叫!
汪星人2 第2次汪汪叫!
汪星人2 第3次汪汪叫!
**********我是分割线5**********
大旺财 和 小旺财 都在汪汪叫!
Process finished with exit code 0
已经存在 public void bark(String dog1Name, String dog2Name) 这个参数类型相同但参数名称不同的方法,如果将两个参数顺序对调,即变为 public void bark(String dog2Name, String dog1Name) ,编译器会不会报错呢?答案是一定会报错
!因为编译器只认参数类型,不管参数名称如何命名。
方法重载需要满足的条件和要求:
- 被重载的方法必须改变参数列表。
- 被重载的方法可以改变返回类型。
- 被重载的方法可以改变访问修饰符。
- 被重载的方法可以声明新的或更广的检查异常。
- 方法能够在同一个类中或者在一个子类中被重载。
2、重写
重写(Overriding)
:子类可继承父类中的方法,而不需要重新编写相同的方法。但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,在方法名、参数列表、返回类型都相同的情况下,对方法体进行修改或重写。方法重写又称方法覆盖。但要注意子类函数的访问修饰权限不能少于父类的。
【重写例子(书中例子,有变动)】
核心代码:
/**
* 猎犬类
*/
public class Hound extends Dog {
/**
* 狗叫
*/
public void bark() {
System.out.println("请叫我狗子哥,品种是猎犬,汪汪!");;
}
/**
* 狗叫
* @param dog1Name 狗子1的名称
* @param dog2Name 狗子2的名称
*/
@Override
public void bark(String dog1Name, String dog2Name) {
super.bark(dog1Name, dog2Name);
}
/**
* 狗子闻到气味
*/
public void sniff() {
System.out.println("猎犬闻到骨头的香味");
}
}
为什么会出现下面这个报错呢?书本例子是这么写的呀!其实,这只是规范检查问题,规范写法是要在方法上面添加注解 @Override
的,不写,编译一样能通过。
主函数:
public class OverridingApp {
public static void main(String[] args) {
Dog dog = new Hound();
System.out.println("**********我是分割线1**********");
dog.bark();
System.out.println("**********我是分割线2**********");
dog.bark("大黄", "小黄");
}
}
运行结果:
D:\Develop\JDK\jdk1.8.0_281\bin\java.exe
**********我是分割线1**********
请叫我狗子哥,品种是猎犬,汪汪!
**********我是分割线2**********
大黄 和 小黄 都在汪汪叫!
Process finished with exit code 0
方法重写需要满足的条件和要求:
- 参数列表必须完全与被重写方法的参数列表相同。
- 返回类型必须完全与被重写方法的返回类型相同。
- 访问级别的限制性一定不能比被重写方法的限制性强。
- 访问级别的限制性可以比被重写方法的限制性弱。
- 重写方法一定不能抛出新的检查异常或比被重写的方法声明的检查异常更广泛的检查异常。
- 重写的方法能够抛出更少或更有限的异常(也就是说,被重写的方法声明了异常,但重写的方法可以什么也不声明)。
- 不能重写被标示为 final 的方法。
- 如果不能继承一个方法,则不能重写这个方法。
3、总结
(1)重载是一个编译期的概念,重写是一个运行期的概念。
(2)重载遵循所谓“编译期绑定”,即在编译时根据参数变量的类型判断应该调用哪个方法。
(3)重写遵循所谓“运行期绑定”,即在程序运行时,根据引用变量所指向的实际对象的类型来调用方法。
(4)Java中的方法重写是 Java多态(子类型)1 的实现方式,而Java中的方法重载是 特设多态 2 的一种实现方式。