Java基础——多态
前言:
在面向对象编程中,继承实现了代码的复用,而多态则让代码更具拓展性和灵活性。多态(Polymorphism)——“多种形态”,指的是同一行为在不同对象上表现出不同的结果。
例如:让猫和狗都执行“吃”这个动作,猫吃鱼,狗吃骨头。这就是多态的直观体现。
本文将从多态的概念开始,详细解析Java中多态的实现条件,方法重写规则,向上/向下转型,动态绑定机制,多态的优缺点。
目录
一.多态的概念
多态:通俗来说:就是去完成某个行为,当不同对象去完成时会产生不同状态。
1.1多态的实现条件
在Java中实现条件,必须满足以下三个条件,缺一不可:
1.必须在继承体系下(有父子类关系)
2.子类必须要对父类中的方法重写
3.通过父类的引用调用重写的方法
多态的体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法。
1.2代码示例:
//父类Animal
public class Animal{
String name;
int age;
public Animal(String name,int age){
this.name=name;
this.age=age;
}
public void eat(){
System.out.println(name+"吃饭");
}
}
//子类Cat
public class Cat{
public void Cat(String name,int age){
super(name,age);
}
@Override
public void eat(){
System.out.println(name+"吃鱼");
}
}
// 子类 Dog
public class Dog extends Animal {
public Dog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println(name + "吃骨头~~~");
}
}
//测试
public class Test{
//形参为父类自用,可以接受任何子类对象
public static void eat(Animal a){
a.eat();
}
public static void main(String[] args) {
Cat cat = new Cat("元宝", 2);
Dog dog = new Dog("小七", 1);
eat(cat); // 元宝吃鱼
eat(dog); // 小七吃骨头~~~
}
关键点:eat(Animal a)方法在编写时并不知道传入的具体是Cat还是Dog,只有在程序运行时,根据实际传入的对象类型,才决定调用哪个eat方法。这就是运行时多态
二.方法重写(Override)
重写:子类对父类的非静态,非private,非final,非构造方法的实现过程进行重写编写。外壳不变,核心重写。
2.1重写规则:
| 规则 | 说明 |
| 方法名,参数列表,返回值类型 |
必须完全一致(返回值类型可以是子类类型 ——协变返回类型) |
| 访问权限 |
不能比父类中被重写的方法的访问权限更低 (父类是public,子类不能是protected) |
| 异常 | 不能抛出比父类更宽泛的检擦异常 |
| 不能被重写的方法 | static,private,final修饰的方法,以及构造方法 |
| 注解 | 使用@Override |
2.2重写vs重载
| 对比点 | 重写(Override) | 重载(Overload) |
| 发生范围 | 子类和父类之间 | 同一个类中 |
| 方法名 | 必须相同 | 必须相同 |
| 参数列表 | 必须相同 | 必须不同(个数,类型,顺序) |
| 返回值 | 相同或子类类型(协变) | 可以不同 |
| 访问权限 | 不能更严格 | 无要求 |
| 修饰符 | 不能是static/private/final | 无要求 |
| 绑定时机 | 运行时(动态绑定) | 编译时(静态绑定) |
2.3重写的设计原则
对于已经投入使用的类,尽量不要修改。可以新建以一个类,重写需要改变的方法。
例如:旧手机来电只显示号码,新手机需要显示头像和地区。不应该修改老手机类(避免影响仍在使用的客户),而是创建新手机类,重写来电方法。
三.向上转型与向下转型
3.1向上转型(Upcasting)
创建一个子类对象,将其当成父类对象来使用。
语法: 父类类型 对象名 = new 子类类型 ()
使用场景:直接赋值
方法传参(上面的eat(Animal a))
方法返回(返回父类类型,实际返回子类对象)
public static Animal buyAnimal(String var) {
if ("狗".equals(var)) {
return new Dog("狗狗", 1);
} else if ("猫".equals(var)) {
return new Cat("猫猫", 1);
}
return null;
}
优点:让代码实现更简单灵活
缺点:不能调用子类特有的方法
3.2向下转型(Downcasting)
将向上转型后的父类引用还原为子类对象,以便调用子类特有的方法。
Animal animal = new Cat("元宝", 2);
// animal.bark(); // 编译错误,Animal 中没有 bark
Cat cat = (Cat) animal; // 向下转型
cat.mew(); // 可以调用 Cat 特有的方法
风险:如果转型失败,运行时抛出ClassCastException
Animal animal = new Dog("小七", 1);
Cat cat = (Cat) animal; // 编译通过,但运行时报错!
安全转换:使用instanceof关键字判断。
if(animal instanceof Cat){
Cat cat=(Cat) animal;
cat.new();
}
instanceof用于判断对象是否是某个类(或其子类)的实例,返回true后再转型是安全的。
四.动态绑定和静态绑定
静态绑定:编译时就能确定调用的方法。典型带面:方法重载,static方法,private方法,final方法。
动态绑定:编译时不确定,运行时根据实际对象类型决定调用的方法。典型代表:多态下的重写方法。
五.多态的优缺点
5.1优点一:降低圈复杂度,避免大量的if-else
假设我们需要绘制多个形状(圆形,矩形,花朵)。如果不适用多态,代码会充斥if-else:
public static void drawShapes() {
Rect rect = new Rect();
Cycle cycle = new Cycle();
Flower flower = new Flower();
String[] shapes = {"cycle", "rect", "cycle", "rect", "flower"};
for (String shape : shapes) {
if (shape.equals("cycle")) {
cycle.draw();
} else if (shape.equals("rect")) {
rect.draw();
} else if (shape.equals("flower")) {
flower.draw();
}
}
}
使用多态后,代码变得简洁:
public static void drawShapes() {
Shape[] shapes = {new Cycle(), new Rect(), new Cycle(), new Rect(), new Flower()};
for (Shape shape : shapes) {
shape.draw(); // 多态:每个形状自己知道怎么画
}
}
圈复杂度:粗略等于代码中条件语句和循环语句的个数。多态显著降低圈复杂度,提升代码可读性和可维护性。
5.2优点二:可拓展能力强
如果新增一个Triangle形状,多态方式只需要新增类
class Triangle extends Shape {
@Override
public void draw() {
System.out.println("△");
}
}
// 在数组中直接添加 new Triangle() 即可
5.3 缺点
代码可读性略有下降(需要理解继承和重写关系)
性能上有损耗(动态绑定需要额外查找)
不能调用子类特有的方法(需要向下转型,有风险)
总结
| 核心概念 | 要点 |
| 多态条件 | 继承+重写+父类引用调用子类对象 |
| 重写规则 |
方法签名一直,访问权限不能更加严格 不能用static/private/final |
| 向上转型 | 子类对象赋给父类引用,灵活但丢失子类特有方法 |
| 向下转型 | 强制转回子类,需要instanceof保证安全 |
| 动态绑定 | 运行时确定调用哪个方法,是多态的基础 |
| 多态优点 | 构造方法中调用可重写的方法 |
多态是java面向对象三大特性之一(封装,继承,多态)。
更多推荐

所有评论(0)