java SE -- 多态

引文

   在面向对象的世界里,Java多态如同棱镜折射阳光:一段父类引用的代码,却能投射出子类各异的行为光谱。编译时多态以方法重载编织参数组合的变奏曲,运行时多态则借动态绑定让继承树上的每个节点绽放独特芬芳。

当父类变量轻触子类对象,JVM的虚方法表便化作隐形指挥家,让每个重写的方法在内存中跳起专属的芭蕾。这种“形而上”的智慧,既成全了策略模式中算法的自由置换,也铸就了工厂模式里对象的灵动新生。开发者只需执掌继承、重写与向上转型的密钥,便能解锁“万物皆对象”的终极隐喻——同一行animal.speak(),可以是虎啸深林,亦能化燕语雕梁。

多态以抽象为舟,载着代码穿越具象的河流,在扩展性与维护性之间架起黄金桥梁。它不仅是技术规则,更是将生物多样性写入数字基因的诗意尝试:在类型系统的疆域里,演绎着“一即是全”的哲学寓言


目录

​编辑

引文

一、多态的概念

二、多态的实现

1、继承关系

2、 方法重写

(1)方法重写的规则

(2)方法重写与方法重载

3、向上转型与向下转型

(1)向上转型

A、向上转型的方式

 B、向上转型后的特点

(2)向下转型

A、向下转型的必要性

B、向下转型的实现步骤

C、向下转型的风险 

必须显式检查

(3)对比 


一、多态的概念

   多态(Polymorphism)是面向对象编程的三大核心特性之一(另两个是封装和继承),它允许不同类的对象对同一方法或操作做出不同的响应。简单来说,多态意味着“同一接口,多种实现”。具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。

举个例子吧:

   打印机分为黑白打印机和彩色打印机,它们都可以实现打印,但是黑白打印机显然只能打印出黑白两种颜色,而彩色打印机却可以打印出多种颜色。

  这里还是用经典的Animal和Dog、Cat来实现代码:

   在这串代码中,Animal是父类(基类),Dog类和Cat类是Animal的两个子类。

   可以看到,在Animal中,我们定义了三个成员变量,并通过构造方法对其初始化;接着,我们又定义了一个公共方法——eat。

   在两个子类中,我们则通过super关键字,将传给它们构造方法的参数传递给它们父类(Animal)的构造方法,对Animal中定义的三个成员变量进行初始化。

   在两个子类中,我对Animal中定义的方法进行了重写:

   如此,我们在主类First的主方法中,通过向上转型,将Dog实例和Cat实例的对象引用,赋给父类对象引用(Animal)。

   然后,我们通过父类对象引用,引用子类对象,并调用子类对象中的eat方法:

   我在这三个类的构造方法中,都添加了一句打印语句,通过运行结果可以看到,三个构造方法的运行顺序。“Animal created”运行两次的原因是:创建了两次实例,每创建一次实例,参数都会通过子类的构造方法,传递给父类的构造方法。

   通过运行结果,可以清晰地看出,两个子类中的eat方法都成功运行,并且运行结果并不是父类(Animal)中的eat方法的结果,而是两个子类中定义的eat方法的运行结果。

   这就是经典的多态了。


二、多态的实现

实现运行时多态需要满足:

  1. 继承关系:存在父类与子类的层次结构。

  2. 方法重写:子类必须重写父类的方法。

  3. 向上转型:父类引用指向子类对象(如 Animal a = new Cat())。

   想象“动物”都有“叫”的行为,但不同动物叫声不同。多态允许我们统一调用 animal.makeSound(),而实际执行的是猫、狗或鸟的叫声,具体取决于对象类型。这种“同一行为,不同表现”的特性,就是多态的威力。

1、继承关系

   所谓继承,就是将多个类的共同特点抽取出来,放在一个事类中。拥有这些共同特点的类,可以通过extends关键字,与这个类达成继承关系。这个被继承的类,就是父类(或基类),继承父类成员变量或成员方法的类,就是子类(或派生类)。

   在之前的篇章——“java SE -- 继承”中,我花费五千多字,详细讲解过继承,在这里不做赘述,有兴趣的朋友可以移步——

java SE -- 继承-优快云博客

2、 方法重写

   Java中的方法重写是指子类重新定义父类中已有的方法,以提供更适合自身功能的实现。这是面向对象编程中实现多态性的关键机制之一。

(1)方法重写的规则

  1. 方法签名必须相同
    方法名、参数列表(类型、顺序、数量)必须与父类方法完全一致。

     

  2. 返回类型兼容

    • 基本类型:必须与父类方法返回类型相同。

    • 引用类型:可以是父类方法返回类型的子类(协变返回类型,Java 5+支持)。

      class Animal {}
      class Dog extends Animal {}  // Dog 是 Animal 的子类
      
      class Parent {
          public Animal create() {  // 父类方法返回 Animal
              return new Animal();
          }
      }
      
      class Child extends Parent {
          @Override
          public Dog create() {  // 子类重写方法返回 Dog(Animal 的子类)
              return new Dog();  // 协变返回类型!
          }
      }

  3. 访问权限不能更严格
    子类方法的访问修饰符必须 ≥ 父类方法的权限(例如,父类方法是 protected,子类可以是 public 或 protected,但不能是 private)。

  4. 异常处理限制
    子类方法抛出的检查异常(Checked Exception)不能比父类方法更宽泛,可以不抛出异常或抛出更具体的异常。

  5. @Override 注解
    建议使用 @Override 注解标记重写的方法,编译器会检查是否符合重写规则。

(2)方法重写与方法重载

特性重写 (Override)重载 (Overload)
作用范围子类与父类之间同一类中
方法签名必须相同必须不同(参数列表不同)
返回类型必须兼容可以不同
访问权限不能更严格可以不同
多态性基于对象类型(运行时多态)基于参数类型(编译时多态)

注意:

  1. 静态方法不能被重写
    静态方法属于类,若子类定义同名静态方法,属于隐藏,而非重写。

  2. final方法禁止重写
    父类中声明为 final 的方法不可被子类重写。

  3. 私有方法无法重写
    父类的私有方法对子类不可见,因此不能重写。

3、向上转型与向下转型

   在 Java 中,向上转型是子类对象隐式转换为父类类型的过程,无需显式类型转换符,是实现多态的基础。

(1)向上转型

A、向上转型的方式

1.直接赋值

将子类对象直接赋值给父类引用变量:

class Animal {}
class Cat extends Animal {} // Cat 是 Animal 的子类

public class Main {
    public static void main(String[] args) {
        // 向上转型:子类对象赋值给父类引用
        Animal animal = new Cat(); 
    }
}

这种方式十分安全,无需进行强制类型转换,java会自动完成类型提升。因为子类对象一定是父类类型(如狗一定是动物)。

2.方法参数传递

将子类对象作为参数传递给接收父类类型的方法:

class Animal {}
class Dog extends Animal {}

public class Main {
    // 方法参数类型为父类 Animal
    public static void feed(Animal animal) {
        System.out.println("喂食动物");
    }

    public static void main(String[] args) {
        Dog dog = new Dog();
        feed(dog); // 向上转型:Dog → Animal
    }
}

3. 集合存储

将不同子类对象存入父类类型的集合中:

import java.util.ArrayList;
import java.util.List;

class Animal {}
class Bird extends Animal {}
class Fish extends Animal {}

public class Main {
    public static void main(String[] args) {
        List<Animal> zoo = new ArrayList<>();
        zoo.add(new Bird()); // 向上转型:Bird → Animal
        zoo.add(new Fish()); // 向上转型:Fish → Animal
    }
}

 B、向上转型后的特点
  1. 只能访问父类成员
    父类引用无法直接调用子类特有的方法或属性:
    class Animal {
        public void eat() { System.out.println("动物吃东西"); }
    }
    
    class Cat extends Animal {
        public void catchMouse() { System.out.println("猫抓老鼠"); } // 子类特有方法
    }
    
    public class Main {
        public static void main(String[] args) {
            Animal animal = new Cat();
            animal.eat();          // 可以调用父类方法
            // animal.catchMouse(); // 编译错误!父类引用无法访问子类特有方法
        }
    }

  2. 多态性
    如果子类重写了父类方法,调用时会执行子类的方法:
    class Animal {
        public void makeSound() { System.out.println("动物叫"); }
    }
    
    class Dog extends Animal {
        @Override
        public void makeSound() { System.out.println("汪汪汪"); } // 重写父类方法
    }
    
    public class Main {
        public static void main(String[] args) {
            Animal animal = new Dog(); // 向上转型
            animal.makeSound(); // 输出“汪汪汪”(实际调用子类方法)
        }
    }

(2)向下转型

   在 Java 中,向下转型(Downcasting)是将父类类型的引用强制转换为其子类类型的过程。与向上转型不同,向下转型需要显式类型转换符((子类名)),并且存在类型不安全的风险

A、向下转型的必要性

   当父类引用指向一个子类对象时,虽然可以通过多态调用重写的方法,但无法直接访问子类特有的成员(方法或属性)。此时需要通过向下转型恢复对象的子类类型,以调用其特有功能。

class Animal {
    public void eat() { System.out.println("动物吃东西"); }
}

class Cat extends Animal {
    @Override
    public void eat() { System.out.println("猫吃鱼"); }

    // 子类特有方法
    public void catchMouse() { 
        System.out.println("猫抓老鼠"); 
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Cat(); // 向上转型
        animal.eat();              // 输出“猫吃鱼”(多态)

        // animal.catchMouse();     // 编译错误!Animal引用无法直接调用子类方法
        Cat cat = (Cat) animal;    // 向下转型:强制转换为Cat类型
        cat.catchMouse();          // 输出“猫抓老鼠”
    }
}
B、向下转型的实现步骤
  1. 强制类型转换

    使用 (子类名) 显式转换:

    父类引用 变量名 = new 子类();  // 向上转型
    子类名 子类变量 = (子类名) 父类引用; // 向下转型
  2. 类型检查(instanceof

    为避免 ClassCastException,转换前必须用 instanceof 检查实际类型:

    if (父类引用 instanceof 子类名) {
        子类名 子类变量 = (子类名) 父类引用;
        // 调用子类特有方法
    }
  3. 示例:

    Animal animal = new Cat(); // 假设 animal 实际指向 Cat 对象
    
    if (animal instanceof Cat) {
        Cat cat = (Cat) animal; // 安全向下转型
        cat.catchMouse();       // 成功调用
    } else {
        System.out.println("无法转换");
    }
C、向下转型的风险 
  1. ClassCastException

    如果父类引用实际指向的对象与目标子类不匹配,会抛出运行时异常:

    Animal animal = new Dog(); // 实际是 Dog 对象
    Cat cat = (Cat) animal;    // 抛出 ClassCastException
必须显式检查

未使用 instanceof 检查直接转换是危险的:

// 危险代码(可能崩溃)
Animal animal = new Animal(); // 父类对象(不是子类)
Cat cat = (Cat) animal;       // 运行时异常
(3)对比 
特性向下转型向上转型
方向父类 → 子类子类 → 父类
安全性不安全(需显式检查和转换)安全(自动完成)
显式/隐式显式(需强制类型转换符)隐式(无需代码干预)
用途恢复子类特有功能统一接口处理对象

注意:

  1. 优先使用多态
    如果频繁需要向下转型,可能说明代码设计有问题,应尝试通过多态或接口优化。

  2. 避免滥用 instanceof
    过多类型检查会导致代码冗余,考虑使用设计模式(如工厂模式、策略模式)替代。

  3. 继承体系的合理性
    确保向下转型的目标类型是实际对象的子类,否则转换必然失败。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

跨过雄关从头越

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值