黑马学习笔记Day09

2025-11-13 黑马程序员学习笔记 55 0

Java 面向对象高级知识总结:多态、枚举类与抽象类

在 Java 面向对象编程中,多态、枚举类和抽象类是进阶核心知识点,它们分别解决了“行为统一与扩展”“固定常量定义”“通用模板与强制规范”的问题。本文基于学习文档,系统梳理三者的核心特性、用法及实战案例,助力高效掌握面向对象高级用法。

一、多态

1. 核心概念

多态是面向对象四大特性之一,特指继承体系中方法重写带来的动态多态性。核心语法为“父类引用指向子类对象”,允许一个对象以多种形式表现,实现“同一行为,不同实现”。

2. 语法格式

// 父类类型 变量名 = new 子类类型();
Animal myDog = new Dog();
Animal myCat = new Cat();

3. 核心特点与好处

  • 解耦合:右边子类对象与左边父类引用解耦,便于扩展和维护(如新增子类无需修改原有代码)。
  • 高扩展性:方法形参使用父类类型时,可接收所有子类对象,无需为每个子类单独定义方法。
  • 存在局限:多态下父类引用只能调用父类声明的方法,无法直接调用子类独有方法(需通过类型转换解决)。

4. 多态下的类型转换

(1)自动类型转换(向上转型)

  • 语法:父类变量名 = new 子类();(如 Animal a = new Dog();)。
  • 特点:无需手动转换,是多态的基础形式,子类对象自动向上转型为父类类型。

(2)强制类型转换(向下转型)

  • 语法:子类变量名 = (子类) 父类变量;(如 Dog d = (Dog) a;)。
  • 作用:解决多态下无法调用子类独有方法的问题。
  • 风险:若父类引用的真实对象类型与强转后的子类类型不匹配,会抛出 ClassCastException(类型转换异常)。
  • 推荐操作:强转前用 instanceof 关键字判断对象真实类型,避免异常。

代码示例(类型转换与多态)

// 父类 Animal
abstract class Animal {
    public abstract void sound(); // 抽象方法,子类必须重写
}

// 子类 Dog
class Dog extends Animal {
    @Override
    public void sound() {
        System.out.println("狗叫:汪汪汪");
    }

    // 子类独有方法
    public void watchHome() {
        System.out.println("狗看家护院");
    }
}

// 子类 Cat
class Cat extends Animal {
    @Override
    public void sound() {
        System.out.println("猫叫:喵喵喵");
    }

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

// 测试类
public class TestPolymorphism {
    public static void main(String[] args) {
        // 1. 自动类型转换(多态形式)
        Animal a1 = new Dog();
        Animal a2 = new Cat();

        // 调用重写方法(动态绑定,执行子类逻辑)
        a1.sound(); // 输出:狗叫:汪汪汪
        a2.sound(); // 输出:猫叫:喵喵喵

        // 2. 强制类型转换(调用子类独有方法)
        // 先判断真实类型,再强转
        if (a1 instanceof Dog) {
            Dog d = (Dog) a1;
            d.watchHome(); // 输出:狗看家护院
        }

        if (a2 instanceof Cat) {
            Cat c = (Cat) a2;
            c.catchMouse(); // 输出:猫抓老鼠
        }

        // 3. 错误示例(真实类型不匹配,运行时异常)
        // if (a1 instanceof Cat) {
        //     Cat c = (Cat) a1; // 运行报错:ClassCastException
        // }
    }
}

5. 核心小结

  • 多态的前提:继承关系 + 方法重写
  • 方法调用规则:编译看左边(父类),运行看右边(子类真实类型)。
  • 类型转换:向上转型自动完成,向下转型需先通过 instanceof 判断类型。

二、枚举类

1. 核心概念

枚举(enum)是一种特殊的类,用于定义一组固定的常量(如一周天数、四季、状态码等),确保常量的唯一性和安全性。

2. 基本语法

// 修饰符 enum 枚举类名 { 常量1, 常量2, ...; 其他成员 }
enum Day {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
}

3. 核心特点(反编译验证)

通过 javap 命令反编译枚举类字节码,可发现以下特性:

  • 枚举类默认被 final 修饰,不可被继承
  • 枚举类默认继承 java.lang.Enum 类(无需显式声明)。
  • 枚举常量是 public static final 修饰的枚举类实例,必须放在类的第一行。
  • 枚举类的构造器默认是 private(写不写都私有),对外不能创建对象
  • 编译器自动生成 values()(返回所有常量数组)和 valueOf(String name)(根据名称获取常量)两个静态方法。

4. 拓展用法(带成员变量与方法)

枚举类可添加成员变量、带参构造器和普通方法,让常量具备更丰富的功能。

代码示例(增强版枚举类)

// 枚举类:表示一周的天数,包含是否工作日的属性
enum Day {
    // 枚举常量:必须与带参构造器参数匹配
    MONDAY(true), TUESDAY(true), WEDNESDAY(true),
    THURSDAY(true), FRIDAY(true),
    SATURDAY(false), SUNDAY(false);

    // 成员变量(私有 final,确保不可变)
    private final boolean isWorkDay;

    // 带参构造器(必须私有)
    private Day(boolean isWorkDay) {
        this.isWorkDay = isWorkDay;
    }

    // 普通方法:获取是否为工作日
    public boolean isWorkDay() {
        return isWorkDay;
    }
}

// 测试类
public class TestEnum {
    public static void main(String[] args) {
        // 1. 遍历所有枚举常量(values() 方法)
        for (Day day : Day.values()) {
            System.out.println(day + " 是否工作日:" + day.isWorkDay());
        }

        // 2. 根据名称获取枚举常量(valueOf() 方法)
        Day friday = Day.valueOf("FRIDAY");
        System.out.println("\n" + friday + " 是否工作日:" + friday.isWorkDay());

        // 3. 枚举常量与 switch 结合(推荐用法)
        Day today = Day.MONDAY;
        switch (today) {
            case MONDAY:
            case TUESDAY:
            case WEDNESDAY:
            case THURSDAY:
            case FRIDAY:
                System.out.println("今日工作日,努力搬砖!");
                break;
            case SATURDAY:
            case SUNDAY:
                System.out.println("今日休息日,好好放松!");
                break;
        }
    }
}

5. 适用场景

  • 定义固定集合的常量(如性别:男/女、支付状态:成功/失败/待支付)。
  • 替代 static final 常量组(更简洁、类型安全)。
  • switch 语句结合,简化条件判断。

三、抽象类

1. 核心概念

抽象类是用 abstract 关键字修饰的类,不能被实例化,主要用于作为父类,封装子类的通用属性和方法,同时强制子类实现特定抽象方法。

2. 核心语法

(1)抽象类定义

// 抽象类
abstract class 类名 {
    // 成员变量
    // 构造器(用于子类初始化)
    // 普通方法(有具体实现)
    // 抽象方法(无实现,子类必须重写)
    public abstract 返回值类型 方法名(参数列表);
}

(2)抽象方法定义

  • abstract 修饰,只有方法签名,没有方法体(无 {})。
  • 包含抽象方法的类,必须声明为抽象类;抽象类可以不含抽象方法。

3. 核心特点

  • 不能实例化:new 抽象类名() 编译报错,仅作为子类的父类。
  • 子类继承规则:子类必须重写抽象类的所有抽象方法,否则子类需声明为抽象类。
  • 可包含普通成员:抽象类可拥有成员变量、构造器、普通方法(与普通类一致)。
  • 构造器的作用:抽象类的构造器用于初始化子类继承的成员变量,由子类通过 super() 调用。

4. 核心好处与应用场景

(1)好处

  • 代码重用:封装子类的通用逻辑,避免重复编码。
  • 强制规范:通过抽象方法,强制子类实现核心功能,确保子类遵循统一标准。
  • 灵活性:子类可自定义抽象方法的实现,适配不同业务场景。

(2)典型应用场景:模板方法设计模式

模板方法模式是抽象类的核心应用,定义算法的骨架(步骤流程),将部分步骤的实现延迟到子类,确保算法结构不变,同时允许子类灵活扩展。

5. 代码示例(模板方法设计模式)

// 抽象类:议论文模板(定义算法骨架)
abstract class CompositionTemplate {
    // 模板方法:定义写议论文的固定流程(算法骨架)
    public final void write() {
        head(); // 第一步:提出观点(固定实现)
        body(); // 第二步:举证论证(抽象方法,子类实现)
        end();  // 第三步:总结观点(固定实现)
    }

    // 普通方法:固定实现
    public void head() {
        System.out.println("《我的观点》");
        System.out.println("在日常生活中,xxx 是一个重要的话题。我认为,xxx 应该这样做:");
    }

    // 抽象方法:子类必须实现
    public abstract void body();

    // 普通方法:固定实现
    public void end() {
        System.out.println("综上所述,我的观点是正确的。让我们一起践行 xxx!");
    }
}

// 子类:学生实现的议论文(填充具体论证内容)
class StudentComposition extends CompositionTemplate {
    // 重写抽象方法:实现举证论证步骤
    @Override
    public void body() {
        System.out.println("首先,xxx 能带来...(论据一)");
        System.out.println("其次,xxx 可以解决...(论据二)");
        System.out.println("最后,无数案例证明...(论据三)");
    }
}

// 测试类
public class TestTemplate {
    public static void main(String[] args) {
        CompositionTemplate composition = new StudentComposition();
        composition.write(); // 调用模板方法,自动执行固定流程
    }
}

运行结果

《我的观点》
在日常生活中,xxx 是一个重要的话题。我认为,xxx 应该这样做:
首先,xxx 能带来...(论据一)
其次,xxx 可以解决...(论据二)
最后,无数案例证明...(论据三)
综上所述,我的观点是正确的。让我们一起践行 xxx!

6. 核心小结

  • 抽象类的核心价值:定义模板 + 强制规范
  • 抽象方法与普通方法的区别:抽象方法强制子类实现,普通方法提供默认实现。
  • 模板方法用 final 修饰:防止子类修改算法骨架,确保流程统一。

四、综合作业题(含解答)

作业题:多态、枚举与抽象类综合应用

题目需求

  1. 定义抽象父类 Animal,包含抽象方法 sound() 和普通方法 eat()(输出“动物进食”)。
  2. 定义子类 DogCat,重写 sound() 方法,分别添加独有方法 watchHome()catchMouse()
  3. 定义枚举类 AnimalType,包含 DOGCAT 两个常量,用于标识动物类型。
  4. 创建测试类,通过多态创建 DogCat 对象,调用 sound()eat() 方法;通过强制类型转换调用子类独有方法;通过枚举类判断动物类型。

解答代码

// 1. 抽象父类 Animal
abstract class Animal {
    // 抽象方法:动物叫声(子类必须重写)
    public abstract void sound();

    // 普通方法:动物进食(默认实现)
    public void eat() {
        System.out.println("动物进食");
    }
}

// 2. 子类 Dog
class Dog extends Animal {
    @Override
    public void sound() {
        System.out.println("狗叫:汪汪汪");
    }

    // 独有方法:看家
    public void watchHome() {
        System.out.println("狗看家护院");
    }
}

// 3. 子类 Cat
class Cat extends Animal {
    @Override
    public void sound() {
        System.out.println("猫叫:喵喵喵");
    }

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

// 4. 枚举类:动物类型
enum AnimalType {
    DOG("狗"), CAT("猫");

    private final String typeName;

    private AnimalType(String typeName) {
        this.typeName = typeName;
    }

    public String getTypeName() {
        return typeName;
    }
}

// 5. 测试类
public class TestComprehensive {
    public static void main(String[] args) {
        // 多态创建对象
        Animal animal1 = new Dog();
        Animal animal2 = new Cat();

        // 调用继承的普通方法和重写的抽象方法
        System.out.println("=== 动物1行为 ===");
        animal1.eat(); // 继承自父类的方法
        animal1.sound(); // 重写的方法
        judgeType(animal1); // 通过枚举判断类型
        callUniqueMethod(animal1); // 强制类型转换调用独有方法

        System.out.println("\n=== 动物2行为 ===");
        animal2.eat();
        animal2.sound();
        judgeType(animal2);
        callUniqueMethod(animal2);
    }

    // 工具方法:通过枚举判断动物类型
    public static void judgeType(Animal animal) {
        if (animal instanceof Dog) {
            System.out.println("动物类型:" + AnimalType.DOG.getTypeName());
        } else if (animal instanceof Cat) {
            System.out.println("动物类型:" + AnimalType.CAT.getTypeName());
        }
    }

    // 工具方法:强制类型转换调用子类独有方法
    public static void callUniqueMethod(Animal animal) {
        if (animal instanceof Dog) {
            Dog dog = (Dog) animal;
            dog.watchHome();
        } else if (animal instanceof Cat) {
            Cat cat = (Cat) animal;
            cat.catchMouse();
        }
    }
}

运行结果

=== 动物1行为 ===
动物进食
狗叫:汪汪汪
动物类型:狗
狗看家护院

=== 动物2行为 ===
动物进食
猫叫:喵喵喵
动物类型:猫
猫抓老鼠

五、总结

  • 多态:基于继承和方法重写,实现“父类引用指向子类对象”,核心价值是解耦和扩展。
  • 枚举类:定义固定常量集合,类型安全、语法简洁,适合替代常量组和状态标识。
  • 抽象类:不能实例化,用于封装通用逻辑和强制子类规范,模板方法模式是其典型应用。
  • 三者的核心关联:多态可结合抽象类(抽象方法强制重写)和枚举类(常量类型判断),构建灵活、规范的面向对象体系。

掌握这三个知识点,能大幅提升 Java 代码的规范性、扩展性和可维护性,是进阶中高级开发的必备基础。