Appearance
11.5 多态
多态的概念
多态(Polymorphism)是面向对象编程的三大特性之一,它允许不同对象对同一消息做出不同的响应。多态是通过方法重写和继承实现的。
多态的实现
在 Java 中,多态的实现主要通过以下方式:
- 继承:子类继承父类
- 方法重写:子类重写父类的方法
- 父类引用指向子类对象:使用父类类型的变量引用子类类型的对象
示例:基本多态
java
// 父类
public class Animal {
public void makeSound() {
System.out.println("Animal makes sound");
}
}
// 子类
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Dog barks");
}
}
// 子类
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Cat meows");
}
}
// 测试类
public class PolymorphismExample {
public static void main(String[] args) {
// 父类引用指向子类对象
Animal animal1 = new Dog();
Animal animal2 = new Cat();
// 多态:根据实际类型调用相应的方法
animal1.makeSound(); // 输出 "Dog barks"
animal2.makeSound(); // 输出 "Cat meows"
}
}多态的特点
- 编译时类型与运行时类型:编译时类型是变量声明的类型,运行时类型是变量实际引用的对象类型
- 方法调用:方法调用根据运行时类型决定,而不是编译时类型
- 向上转型:将子类对象赋值给父类引用,是自动的
- 向下转型:将父类引用转换为子类类型,需要显式转换
示例:编译时类型与运行时类型
java
public class PolymorphismExample {
public static void main(String[] args) {
// 编译时类型:Animal,运行时类型:Dog
Animal animal = new Dog();
// 调用的是 Dog 类的 makeSound 方法
animal.makeSound();
// 编译时类型:Animal,运行时类型:Cat
animal = new Cat();
// 调用的是 Cat 类的 makeSound 方法
animal.makeSound();
}
}示例:多态的应用
示例 1:动物类
java
// 父类:Animal
public class Animal {
public void makeSound() {
System.out.println("Animal makes sound");
}
public void eat() {
System.out.println("Animal eats");
}
}
// 子类:Dog
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Dog barks");
}
@Override
public void eat() {
System.out.println("Dog eats bones");
}
}
// 子类:Cat
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Cat meows");
}
@Override
public void eat() {
System.out.println("Cat eats fish");
}
}
// 测试类
public class AnimalExample {
public static void main(String[] args) {
// 创建动物数组
Animal[] animals = new Animal[3];
animals[0] = new Animal();
animals[1] = new Dog();
animals[2] = new Cat();
// 遍历数组,调用方法
for (Animal animal : animals) {
animal.makeSound();
animal.eat();
System.out.println();
}
}
}示例 2:形状类
java
// 父类:Shape
public abstract class Shape {
public abstract void draw();
public abstract double calculateArea();
}
// 子类:Circle
public class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public void draw() {
System.out.println("Drawing a circle");
}
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
}
// 子类:Rectangle
public class Rectangle extends Shape {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public void draw() {
System.out.println("Drawing a rectangle");
}
@Override
public double calculateArea() {
return width * height;
}
}
// 测试类
public class ShapeExample {
public static void main(String[] args) {
// 创建形状数组
Shape[] shapes = new Shape[2];
shapes[0] = new Circle(5);
shapes[1] = new Rectangle(4, 6);
// 遍历数组,调用方法
for (Shape shape : shapes) {
shape.draw();
System.out.println("Area: " + shape.calculateArea());
System.out.println();
}
}
}示例 3:员工类
java
// 父类:Employee
public class Employee {
protected String name;
public Employee(String name) {
this.name = name;
}
public void work() {
System.out.println(name + " is working");
}
public double calculateSalary() {
return 0.0;
}
}
// 子类:Manager
public class Manager extends Employee {
private double bonus;
public Manager(String name, double bonus) {
super(name);
this.bonus = bonus;
}
@Override
public void work() {
System.out.println(name + " is managing the team");
}
@Override
public double calculateSalary() {
return 8000 + bonus;
}
}
// 子类:Developer
public class Developer extends Employee {
private String programmingLanguage;
public Developer(String name, String programmingLanguage) {
super(name);
this.programmingLanguage = programmingLanguage;
}
@Override
public void work() {
System.out.println(name + " is coding in " + programmingLanguage);
}
@Override
public double calculateSalary() {
return 6000;
}
}
// 测试类
public class EmployeeExample {
public static void main(String[] args) {
// 创建员工数组
Employee[] employees = new Employee[3];
employees[0] = new Employee("John");
employees[1] = new Manager("Jane", 2000);
employees[2] = new Developer("Bob", "Java");
// 遍历数组,调用方法
for (Employee employee : employees) {
employee.work();
System.out.println("Salary: " + employee.calculateSalary());
System.out.println();
}
}
}多态的优势
- 代码灵活性:通过多态,可以编写更灵活、更通用的代码
- 代码可扩展性:当添加新的子类时,不需要修改现有代码
- 代码可维护性:将代码逻辑集中在父类,子类只需实现自己的特有逻辑
- 代码复用:通过继承和多态,实现代码复用
示例:多态的优势
java
// 父类:Animal
public class Animal {
public void makeSound() {
System.out.println("Animal makes sound");
}
}
// 子类:Dog
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Dog barks");
}
}
// 子类:Cat
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Cat meows");
}
}
// 新增子类:Bird
public class Bird extends Animal {
@Override
public void makeSound() {
System.out.println("Bird sings");
}
}
// 测试类
public class PolymorphismAdvantage {
// 通用方法,接受任何 Animal 类型
public static void makeAnimalSound(Animal animal) {
animal.makeSound();
}
public static void main(String[] args) {
// 调用通用方法
makeAnimalSound(new Dog());
makeAnimalSound(new Cat());
makeAnimalSound(new Bird()); // 新增子类,不需要修改现有代码
}
}向上转型和向下转型
向上转型
向上转型是将子类对象赋值给父类引用,是自动的。
语法:
java
父类类型 变量名 = new 子类类型();示例:
java
Animal animal = new Dog(); // 向上转型向下转型
向下转型是将父类引用转换为子类类型,需要显式转换。
语法:
java
子类类型 变量名 = (子类类型) 父类引用;示例:
java
Animal animal = new Dog();
Dog dog = (Dog) animal; // 向下转型instanceof 运算符
在向下转型之前,通常使用 instanceof 运算符检查对象的实际类型,避免类型转换错误。
语法:
java
对象 instanceof 类型示例:
java
Animal animal = new Dog();
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
// 调用 Dog 类的特有方法
}示例:向下转型
java
// 父类:Animal
public class Animal {
public void makeSound() {
System.out.println("Animal makes sound");
}
}
// 子类:Dog
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Dog barks");
}
// Dog 类的特有方法
public void fetch() {
System.out.println("Dog fetches the ball");
}
}
// 子类:Cat
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Cat meows");
}
// Cat 类的特有方法
public void scratch() {
System.out.println("Cat scratches the furniture");
}
}
// 测试类
public class DowncastingExample {
public static void main(String[] args) {
// 向上转型
Animal animal1 = new Dog();
Animal animal2 = new Cat();
// 调用父类方法
animal1.makeSound();
animal2.makeSound();
// 向下转型
if (animal1 instanceof Dog) {
Dog dog = (Dog) animal1;
dog.fetch();
}
if (animal2 instanceof Cat) {
Cat cat = (Cat) animal2;
cat.scratch();
}
}
}多态的实现机制
Java 中的多态是通过方法表实现的。每个类都有一个方法表,记录了该类的所有方法。当调用一个方法时,Java 虚拟机会根据对象的实际类型查找相应的方法表,然后调用对应的方法。
方法调用的过程
- 编译时:检查方法是否存在于编译时类型中
- 运行时:根据对象的实际类型,调用相应的方法
常见问题
1. 方法不存在
症状:编译错误:The method methodName() is undefined for the type Parent
解决方案:确保调用的方法在父类中存在
示例:
java
public class Parent {
public void method1() {
}
}
public class Child extends Parent {
public void method2() {
}
}
public class Example {
public static void main(String[] args) {
Parent parent = new Child();
parent.method1(); // 正确:method1 在父类中存在
// parent.method2(); // 错误:method2 在父类中不存在
}
}2. 类型转换错误
症状:运行时错误:ClassCastException
解决方案:在向下转型之前,使用 instanceof 运算符检查对象的实际类型
示例:
java
public class Example {
public static void main(String[] args) {
Animal animal = new Dog();
// 错误:animal 的实际类型是 Dog,不能转换为 Cat
// Cat cat = (Cat) animal;
// 正确:使用 instanceof 检查
if (animal instanceof Cat) {
Cat cat = (Cat) animal;
} else {
System.out.println("animal is not a Cat");
}
}
}3. 静态方法不支持多态
症状:静态方法调用的是编译时类型的方法,而不是运行时类型的方法
解决方案:静态方法不支持多态,它们属于类,不属于对象
示例:
java
public class Parent {
public static void staticMethod() {
System.out.println("Parent static method");
}
}
public class Child extends Parent {
public static void staticMethod() {
System.out.println("Child static method");
}
}
public class Example {
public static void main(String[] args) {
Parent parent = new Child();
parent.staticMethod(); // 输出 "Parent static method",静态方法不支持多态
}
}最佳实践
- 使用抽象类和接口:通过抽象类和接口定义共同的行为
- 合理使用向上转型:将子类对象赋值给父类引用,提高代码的灵活性
- 谨慎使用向下转型:在向下转型之前,使用
instanceof检查对象的实际类型 - 理解多态的实现机制:了解方法表和方法调用的过程
- 设计良好的继承层次:合理设计类的继承层次,提高代码的可维护性
总结
多态是面向对象编程的三大特性之一,它允许不同对象对同一消息做出不同的响应。多态是通过方法重写和继承实现的。
多态的实现方式:
- 继承:子类继承父类
- 方法重写:子类重写父类的方法
- 父类引用指向子类对象:使用父类类型的变量引用子类类型的对象
多态的优势:
- 代码灵活性:编写更灵活、更通用的代码
- 代码可扩展性:添加新的子类时,不需要修改现有代码
- 代码可维护性:将代码逻辑集中在父类
- 代码复用:通过继承和多态,实现代码复用
通过合理使用多态,可以提高代码的灵活性、可扩展性和可维护性。
