学习Java38天

Java 多态 (Polymorphism)

多态是面向对象三大特性中最核心、最灵活的特性。它意味着 "同一行为,多种实现"

一、多态的基本概念

  • 定义:同一种类型的对象(或引用),在不同的情况下表现出不同的行为和状态。

  • 核心表现形式(向上转型):

    java

    复制代码
    父类类型 引用名 = new 子类对象();

    例如

    java

    复制代码
    Animal a = new Dog(); // 多态
    Animal b = new Cat(); // 多态

    这里的 Animal 是编译时类型,而 DogCat 是运行时类型。

  • 多态的前提条件(三者缺一不可)

    1. 有继承关系实现关系(接口)。

    2. 有父类(或接口)引用指向子类对象(即向上转型)。

    3. 有方法的重写(否则多态调用方法没有实际意义)。

二、多态的优势

  1. 降低耦合度,提高扩展性:方法参数或容器(如集合)使用父类类型,可以接受任何子类对象,代码通用性更强。

  2. 便于维护:当需要新增一种子类时,无需修改使用父类引用的代码。

  3. 代码简洁:统一的接口,不同的实现。

示例:体现扩展性的优势

java

复制代码
class Animal {
    public void eat() {
        System.out.println("动物吃东西");
    }
}
class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗吃骨头");
    }
}
class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
}

// 饲养员类 - 核心优势体现
class Keeper {
    // 参数使用父类类型,可以接收任何Animal子类对象
    public void feedAnimal(Animal a) {
        a.eat(); // 运行时执行具体子类的eat方法
    }
}

public class Main {
    public static void main(String[] args) {
        Keeper keeper = new Keeper();
        
        Animal dog = new Dog();
        Animal cat = new Cat();
        
        keeper.feedAnimal(dog); // 输出:狗吃骨头
        keeper.feedAnimal(cat); // 输出:猫吃鱼
        
        // 未来新增一个Tiger类,Keeper类完全无需修改!
        // keeper.feedAnimal(new Tiger());
    }
}

三、多态调用成员的特点(记忆口诀)

这是理解多态行为的关键:

成员类型 编译阶段(看左边) 运行阶段(看右边) 说明
成员变量 检查父类中是否有该变量 访问父类中的该变量 编译和运行都看左边(父类)
成员方法 检查父类中是否有该方法声明 执行子类中重写后的该方法 编译看左边,运行看右边(子类)

示例

java

复制代码
class Fu {
    int num = 10;
    public void show() {
        System.out.println("Fu show");
    }
}
class Zi extends Fu {
    int num = 20;
    @Override
    public void show() {
        System.out.println("Zi show");
    }
    public void special() {
        System.out.println("Zi special");
    }
}

public class Test {
    public static void main(String[] args) {
        Fu obj = new Zi(); // 多态
        
        // 访问成员变量:看左边(Fu)
        System.out.println(obj.num); // 输出:10
        
        // 调用成员方法:编译看左边(Fu),运行看右边(Zi)
        obj.show(); // 输出:Zi show
        
        // obj.special(); // 编译错误!因为编译看左边,Fu类中没有special方法声明
    }
}

四、多态的弊端与解决方案:类型转换

弊端 :在多态的形式下(父类引用),不能调用子类特有的方法或访问子类特有的变量

解决方案 :使用 引用数据类型转换

1. 向上转型 (Upcasting)
  • 自动进行:子类类型转换为父类类型。

  • Animal a = new Dog(); // 安全,因为狗一定是动物。

2. 向下转型 (Downcasting)
  • 强制进行:父类类型转换为子类类型。

  • 语法子类类型 引用名 = (子类类型) 父类引用;

  • 目的 :为了 "还原" 对象的真实子类类型,以便调用子类特有的功能。

示例

java

复制代码
Animal a = new Dog(); // 向上转型
Dog d = (Dog) a;      // 向下转型,将a强制转换为Dog类型
d.lookHome();         // 现在可以调用Dog特有的方法了
3. 转型的风险与安全校验

如果转型的目标类型与对象的真实类型不匹配,会抛出 ClassCastException(类型转换异常)。

java

复制代码
Animal a = new Cat();
Dog d = (Dog) a; // 运行时错误!ClassCastException: Cat cannot be cast to Dog

安全转型:使用 instanceof 关键字

  • 作用 :在转型前进行判断,避免 ClassCastException

  • 语法引用变量 instanceof 数据类型

  • 结果 :返回 boolean 值。判断引用指向的对象是否属于该类型(或该类型的子类)。

示例

java

复制代码
public static void checkAnimal(Animal a) {
    a.eat(); // 所有动物都有的行为
    
    // 类型判断与安全转型
    if (a instanceof Dog) {
        Dog dog = (Dog) a;
        dog.lookHome();
    } else if (a instanceof Cat) {
        Cat cat = (Cat) a;
        cat.catchMouse();
    } else {
        System.out.println("这是其他动物");
    }
}

五、总结与最佳实践

  1. 多态的本质:通过父类/接口引用调用方法,执行时由JVM根据对象的实际类型决定调用哪个版本的方法(动态绑定)。

  2. 使用场景:当需要编写通用代码、处理一组相关对象、或设计可扩展的系统架构时。

  3. 转型原则

    • 能向上转型就向上转型(利用多态,提高代码通用性)。

    • 必须向下转型时才转(需要使用子类特有功能时)。

    • 向下转型前务必用 instanceof 判断,确保类型安全。

java 复制代码
package polymorphism02;

public class Animal {
    private int age;
    private String color;

    public Animal() {
    }

    public Animal(int age, String color) {
        this.age = age;
        this.color = color;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public void eat(String something){
        System.out.println("动物在吃东西");
    }
}
java 复制代码
package polymorphism02;

public class Cat extends Animal{
    public Cat() {
    }

    public Cat(int age, String color) {
        super(age, color);
    }

    @Override
    public void eat(String something){
        System.out.println(getAge()+"岁的"+getColor()+"颜色的猫在吃"+something);
    }

    public void catchMouse(){
        System.out.println("猫抓老鼠");
    }
}
java 复制代码
package polymorphism02;

public class Dog extends Animal{
    public Dog() {
    }

    public Dog(int age, String color) {
        super(age, color);
    }
    @Override
    public void eat(String something){
        System.out.println(getAge()+"岁的"+getColor()+"颜色的狗在吃"+something);
    }
    public void lookHome(){
        System.out.println("狗在看家");
    }
}
java 复制代码
package polymorphism02;

public class Person {
    private String name;
    private int age;

    public Person() {
    }

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    //饲养狗
    /*public void keepPet(Dog dog,String something){
        System.out.println("年龄为"+age+"岁的"+name+"养了一只"+dog.getColor()+"颜色的"+dog.getAge()+"岁的狗");
        dog.eat(something);
    }
    //饲养猫
    public void keepPet(Cat cat,String something){
        System.out.println("年龄为"+age+"岁的"+name+"养了一只"+cat.getColor()+"颜色的"+cat.getAge()+"岁的猫");
        cat.eat(something);
    }*/

    //一个方法能接收使用动物信息
    //方法的形参:可以写这些类的父类
    public void keepPet(Animal a ,String something){
        if (a instanceof Dog d){
            System.out.println("年龄为"+age+"岁的"+name+"养了一只"+d.getColor()+"颜色的"+d.getAge()+"岁的狗");
            d.eat(something);
        }else if (a instanceof Cat c){
            System.out.println("年龄为"+age+"岁的"+name+"养了一只"+c.getColor()+"颜色的"+c.getAge()+"岁的猫");
            c.eat(something);
        }else {
            System.out.println("没有这种动物");
        }

    }
}
java 复制代码
package polymorphism02;

public class Test {
    public static void main(String[] args) {
        //创建对象并调用
        /*Person p = new Person(30, "老王");
        Dog d = new Dog(2,"黑");
        p.keepPet(d,"骨头");

        Person p1 = new Person(26, "老李");
        Cat d1 = new Cat(3,"黄");
        p1.keepPet(d1,"🐟");
        */

        Person p = new Person(30, "老王");
        Dog d = new Dog(2,"黑");
        Cat c = new Cat(3,"黄");
        p.keepPet(d,"骨头");
        p.keepPet(c,"🐟");


    }
}
相关推荐
西岸行者4 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意4 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码4 天前
嵌入式学习路线
学习
毛小茛4 天前
计算机系统概论——校验码
学习
babe小鑫4 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms4 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下4 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。4 天前
2026.2.25监控学习
学习
im_AMBER4 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J4 天前
从“Hello World“ 开始 C++
c语言·c++·学习