本博客将结合《Java核心技术 卷一》以及博主本人知识,为大家介绍Java面向对象程序设计的基本概念——多态。希望能帮助大家更轻松地学习多态。
一.什么是多态
多态,指为不同数据类型的实体提供统一的接口。多态类型,可以将自身所支持的操作套用到其它类型的值上。——百度百科
多态是对象变量的一种状态,使一个类型的变量可以引用父类的对象,也可以引用父类的任何一个子类对象。
简单来讲,就是对象引用具有多种状态。
为了理解这一概念,请看以下代码:
Animal cc = new Animal();
Animal cc = new Dog();
Animal cc = new Cat();
Animal类型的引用变量cc的引用对象可以是Animal类,也可以是Cat、Dog类,这样cc这个类的引用变量就可以是这其中任意一种状态。
二.实现多态
1.实现多态的条件
实现多态,必须满足以下三项条件:
(1) 必须在继承体系下,
要在继承体系下,是因为只有在该体系下,才能进行类型的转型。
(2)子类必须要对父类中方法进行重写
子类要重写父类方法,只有重写方法,才能提供不同的行为,实现多态。
(3)通过父类的引用调用重写的方法(向上转型)
将子类对象的地址赋值给父类类型的引用,然后通过这个父类类型的引用调用重写的函数。在运行时,程序会根据实际指向的对象类型来决定调用哪个版本的函数,这被称为动态绑定或后期绑定。
即
2.重写父类方法
重写(override),也称为覆盖。重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程 进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
例如,以下代码:
class Dog{
...
public void bark(){
System.out.println("wolf!");
}
...
}
class Husky extends Dog{
...
@override
public void bark(){
System.out.println("wowu!");
}
...
}
public class test {
public static void main(String[] args) {
Dog cc = new Husky()
cc.bark();
}
}
如上就是一个简单的重写,让编译器根据cc所指向的实际对象来调用相应的方法,这就是多态的一个简单应用。
ps:重写的方法, 可以使用 @Override 注解来显式指定,有了这个注解能帮我们进行一些合法性校验.。例如不小心 将方法名字拼写错了 (比如写成brak), 那么此时编译器就会发现父类中没有 brak 方法, 就会编译报错, 提示无法构成重写。
ps:注意区分重载(overload)与重写(override),前者参数不同,返回类型可以不同,后者参数要相同,返回类型也要相同,访问限制符不能更私有。
3.向上转移和向下转型
(1)向上转型
字面理解即可,即以子类对象指向父类类型:
父类类型 对象名 = new 子类类型()
Animal cc = new Dog();
向上转型的优点:让代码实现更简单灵活。如以下代码:
// 父类:动物
class Animal {
public void makeSound() {
System.out.println("动物发出声音");
}
}
// 子类:猫
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("喵喵喵");
}
public void catchMouse(){
......
}
}
// 子类:狗
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("汪汪汪");
}
}
public class Test {
public static void main(String[] args) {
// 向上转型示例
Animal animal1 = new Cat(); // Cat向上转型为Animal
Animal animal2 = new Dog(); // Dog向上转型为Animal
// 统一处理不同类型的动物
animalDoSomething(animal1);
animalDoSomething(animal2);
}
// 可以接收任何Animal及其子类的对象
private static void animalDoSomething(Animal animal) {
animal.makeSound(); // 运行时多态,自动调用实际类型的实现
}
}
这里的animalDoSomething()方法统一接收了Dog和Cat两个不同类的变量,让代码更加灵活,同时保证了代码的可扩展性。
向上转型的缺陷:不能调用到子类特有的方法。如上的animalDoSomething()方法就不能调用catchMouse()的方法。这是为了安全,防止cat类调用引发异常。向下转型就是为了解决这一缺陷而存在的。
(2)向下转型
将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的 方法,此时:将父类引用再还原为子类对象即可,即向下转换。如下代码就是说明向下转型的应用场景的:
// 父类:动物
class Animal {
public void makeSound() {
System.out.println("动物发出声音");
}
}
// 子类:猫(新增特有方法)
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("喵喵喵");
}
// 猫的特有方法
public void purr() {
System.out.println("呼噜呼噜...");
}
}
// 子类:狗(新增特有方法)
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("汪汪汪");
}
// 狗的特有方法
public void fetch() {
System.out.println("捡回飞盘");
}
}
public class Test {
public static void main(String[] args) {
// 创建混合动物集合(向上转型)
Animal[] animals = {
new Cat(),
new Dog(),
new Cat()
};
// 处理动物集合
for (Animal animal : animals) {
animal.makeSound(); // 公共方法直接调用
// 向下转型检查(使用instanceof)
if (animal instanceof Cat) {
Cat cat = (Cat) animal; // 显式向下转型
cat.purr(); // 调用猫的特有方法
} else if (animal instanceof Dog) {
Dog dog = (Dog) animal; // 显式向下转型
dog.fetch(); // 调用狗的特有方法
}
}
}
}
instanceof关键字:用于测试一个对象是否是指定类型或其子类型的实例。它返回一个布尔值,如果对象是指定类型(或者其子类型)的实例,则返回 true
;否则返回 false
。
说明:向下转型用的比较少,而且不安全,万一转换失败,如cat调用dog特有方法,运行时就会抛异常。Java中为了提高向下转型的安全性,引入 了instanceof,如果该表达式为true,则可以安全转换。
结语
以上就是本次分享,想要深入学习面向对象,还得多多写代码,自己体验才行,写着写着说不定自己就懂了。好了,下次将介绍抽象类和接口,欢迎关注!