本章节目标:
理解在什么情况下我们需要进行方法覆盖?掌握在满足什么条件的时候构成方法覆盖?什么是多态,代码怎么写?向上转型和向下转型都是什么?多态在开发中有什么作用?
知识框架:
学习方法覆盖之前,我们先来回顾一下方法重载(overload),什么情况下考虑使用方法重载呢?在同一个类当中,如果功能相似,尽可能将方法名定义的相同,这样方便调用的同时代码也会美观。那么,代码满足什么条件的时候能够构成方法重载呢?只要在同一个类当中,方法名相同,参数列表不同(类型、个数、顺序),即构成方法重载。
带着同样的疑问去学习方法覆盖,什么是方法覆盖?什么情况下考虑方法覆盖?代码怎么写的时候就构成了方法覆盖呢?接下来看一段代码:
public class People {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void speakHi(){
System.out.println(this.name + "和别人打招呼!");
}
}
public class ChinaPeople extends People {
//中国人
}
public class AmericaPeople extends People {
//美国人
}
public class PeopleTest {
public static void main(String[] args) {
ChinaPeople cp = new ChinaPeople();
cp.setName("张三");
cp.speakHi();
AmericaPeople ap = new AmericaPeople();
ap.setName("jackson");
ap.speakHi();
}
}
运行结果如下图所示:
图13-1:运行结果
“中国人”调用speakHi()方法希望输出的结果是“你好,我叫张三,很高兴见到你!”,“美国人”调用speakHi()方法更希望输出的结果是“Hi,My name is jackson,Nice to meet you!”,可见ChinaPeople和AmericaPeople从父类中继承过来的speakHi()方法已经不够子类使用了,那这个时候应该怎么办呢?当然,此时就需要使用方法覆盖机制了。请看以下代码:
public class People {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void speakHi(){
System.out.println(this.name + "和别人打招呼!");
}
}
public class ChinaPeople extends People {
public void speakHi(){
System.out.println("你好,我叫"+this.getName()+",很高兴认识你!");
}
}
public class AmericaPeople extends People {
public void speakHi(){
System.out.println("Hi,My name is "+this.getName()+",Nice to meet you!");
}
}
public class PeopleTest {
public static void main(String[] args) {
ChinaPeople cp = new ChinaPeople();
cp.setName("张三");
cp.speakHi();
AmericaPeople ap = new AmericaPeople();
ap.setName("jackson");
ap.speakHi();
}
}
运行结果如下图所示:
图13-2:方法覆盖之后的运行结果
以上程序中ChinaPeople和AmericaPeople将从People类中继承过来的speakHi()方法进行了覆盖,我们也看到了当speakHi()方法发生覆盖之后,子类对象会调用覆盖之后的方法,不会再去调用之前从父类中继承过来的方法。
那么,到底在什么情况下我们会考虑使用方法覆盖呢?通过以上内容的学习,我们了解到只有当从父类中继承过来的方法无法满足当前子类业务需求的时候,需要将父类中继承过来的方法进行覆盖。换句话说,父类中继承过来的方法已经不够用了,子类有必要将这个方法重新再写一遍,所以方法覆盖又被称为方法重写。当该方法被重写之后,子类对象一定会调用重写之后的方法。
那么,当程序具备哪些条件的时候,就能构成方法覆盖呢?
● 方法覆盖发生在具有继承关系的父子类之间,这是首要条件;
● 覆盖之后的方法与原方法具有相同的返回值类型、相同的方法名、相同的形式参数列表;
● 另外,在使用方法覆盖的时候,需要有哪些注意事项呢?
● 由于覆盖之后的方法与原方法一模一样,建议在开发的时候采用复制粘贴的方式,不建议手写,因为手写的时候非常容易出错,比如在Object类当中有toString()方法,该方法中的S是大写的,在手写的时候很容易写成小写tostring(),这个时候你会认为toString()方法已经被覆盖了,但由于方法名不一致,导致最终没有覆盖,这样就尴尬了;
● 私有的方法不能被继承,所以不能被覆盖;
● 构造方法不能被继承,所以也不能被覆盖;
● 覆盖之后的方法不能比原方法拥有更低的访问权限,可以更高(学习了访问控制权限修饰符之后你就明白了);
● 覆盖之后的方法不能比原方法抛出更多的异常,可以相同或更少(学习了异常之后就明白了);
● 方法覆盖只是和方法有关,和属性无关;
● 静态方法不存在覆盖(不是静态方法不能覆盖,是静态方法覆盖意义不大,学习了多态机制之后就明白了);
以上的注意事项还需要大家记忆,多下点功夫吧。接下来我们再来看一段代码,对方法覆盖加深一下印象,业务需求是这样的:定义一个动物类,所有动物都有移动的行为,其中猫类型的对象在移动的时候输出“猫在走猫步!”,鸟儿类型的对象在移动的时候输出“鸟儿在飞翔!”,但是猫类型的对象具有一个特殊的行为,抓老鼠,这个行为不是所有动物对象都有的,是猫类型对象特有的:
public class Animal {
public void move(){
System.out.println("动物在移动!");
}
}
public class Cat extends Animal{
public void move(){
System.out.println("猫在走猫步!");
}
public void catchMouse(){
System.out.println("猫抓老鼠!");
}
}
public class Bird extends Animal{
public void move(){
System.out.println("鸟儿在飞翔!");
}
}
public class Test {
public static void main(String[] args) {
Cat cat = new Cat();
cat.move();
cat.catchMouse();
Bird bird = new Bird();
bird.move();
}
}
运行结果如下图所示:
图13-3:方法覆盖演示
对方法覆盖总结一下,当父类中继承过来的方法无法满足当前子类业务需求的时候,子类有必要将父类中继承过来的方法进行覆盖/重写。方法覆盖发生在具有继承关系的父子类之间,方法覆盖的时候要求相同的返回值类型、相同的方法名、相同的形式参数列表。方法覆盖之后子类对象在调用的时候一定会执行覆盖之后的方法。