JAVA中的抽象类

文章目录


一、抽象类的定义

如果一个类不能表示一个具体的对象,此时就可以被定义为抽象类,从语法上来讲使用abstract来修饰

抽象类(abstract class)就像一个半成品模板,它定义了一组子类必须遵循的规则(方法、属性),但自己又不完整,不能直接拿来用(无法实例化),必须由子类继承并补充完整(实现抽象方法)后才能使用。

举个生活例子:"交通工具"就是一个抽象概念,它具有行驶这个通用行为,但是你没法直接创建一个交通工具实体;而"汽车""自行车"继承了交通工具,并具体实现了行驶(汽车烧油跑,自行车蹬着邹),这就是抽象类的核心思想。


二、抽象类的关键特点

  1. 不能实例化:无法直接创建抽象类的对象(比如new 交通工具()是错误的)。

  2. 包含抽象方法:抽象方法只有方法声明(定义要做什么),没有方法体(没说"怎么做"),必须由子类重写实现,前面需要加关键字abstract表示抽象方法,否则就不是抽象方法,必须存在方体。

  3. 可以包含普通方法/属性:抽象类也能有已实现的普通方法、成员变量,供子类直接使用(复用代码)。

  4. 子类必须实现所有抽象方法:除非子类也是抽象类,否则必须重写父类所有抽象方法,否则编译报错。

  5. 如果一个类当中存在抽象方法,那么这个类一定是抽象类。如果一个类是抽象类,这个类当中可以没有抽象方法

  6. 定义抽象类

java 复制代码
//用abstract关键字声明抽象类
public abstract class Vehicle {

    //普通属性:所有交通工具都有名称
    protected String name;

    //普通方法:已实现的通用逻辑,子类可直接用
    public void setName(String name) {
        this.name = name;
    }

    //抽象方法:只有声明,没有方法体(用;结尾)
    //定义了行驶这个规则,但没说具体怎么行驶
    public abstract void run();
}
  1. 子类继承并实现抽象方法
java 复制代码
//汽车类继承抽象类Vehicle
public class Car extends Vehicle{
    //必须实现父类的抽象方法run()
    @Override
    public void run() {
        System.out.println(name + "燃油,四轮行驶");
    }
}
//自行车类继承抽象类Vehicle
public class Cycle extends Vehicle {
    //必须实现父类的抽象方法run()
    @Override
    public void run() {
        System.out.println(name + "人力,两轮驱动");
    }
}

如果不实现父类的抽象方法则会发生编译错误:

使用子类(抽象类无法进行实例化)

java 复制代码
public class Test {
    public static void main(String[] args) {
        //错误:抽象类不能实例化
        //Vehicle vehicle = new Vehicle();

        //正确:实例化子类
        Vehicle car = new Car();
        car.setName("特斯拉");
        car.run();

        Vehicle bicycle = new Cycle();
        bicycle.setName("自行车");
        bicycle.run();
    }
}

三、抽象类的使用场景

  • 代码复用 + 规范约束:当多个类共同的属性 / 方法(可抽成抽象类的普通成员),但核心行为实现不同(抽成抽象方法)时,用抽象类统一规范,避免子类 "各自为政"。
  • 定义模板方法:抽象类实现核心流程(模板方法),把可变的步骤留给子类实现(比如 "制作饮品" 的模板:烧水→冲泡→装杯,其中 "冲泡" 是抽象方法,咖啡 / 茶子类分别实现)。

3.1 模板方法模式

这是抽象类最核心、最典型的应用场景,没有之一。

核心思想:抽象类定义一个算法的骨架(模板),把算法中可变的步骤延迟到子类实现,固定的通用步骤则在抽象类中实现,保证算法结构不变,同时能让子类灵活定制部分步骤。

代码示例(冲饮品场景)

java 复制代码
// 抽象类:定义冲饮品的通用模板
public abstract class Beverage {
    // 模板方法:固定算法流程(不能被子类修改,用final保护)
    public final void prepareBeverage() {
        boilWater();       // 固定步骤1:烧水
        brew();            // 可变步骤:冲泡(子类实现)
        pourInCup();       // 固定步骤2:装杯
        addCondiments();   // 可变步骤:加调料(子类实现)
    }

    // 固定方法:所有饮品都要烧水
    private void boilWater() {
        System.out.println("烧开水(100℃)");
    }

    // 固定方法:所有饮品都要装杯
    private void pourInCup() {
        System.out.println("倒入杯中");
    }

    // 抽象方法:冲泡(咖啡/茶的实现不同)
    protected abstract void brew();

    // 抽象方法:加调料(咖啡加糖奶,茶加柠檬/蜂蜜)
    protected abstract void addCondiments();
}

// 咖啡子类:实现抽象方法
public class Coffee extends Beverage {
    @Override
    protected void brew() {
        System.out.println("冲泡咖啡粉");
    }

    @Override
    protected void addCondiments() {
        System.out.println("加方糖和牛奶");
    }
}

// 茶子类:实现抽象方法
public class Tea extends Beverage {
    @Override
    protected void brew() {
        System.out.println("冲泡茶叶");
    }

    @Override
    protected void addCondiments() {
        System.out.println("加柠檬片");
    }
}

// 测试
public class TestTemplate {
    public static void main(String[] args) {
        Beverage coffee = new Coffee();
        System.out.println("制作咖啡:");
        coffee.prepareBeverage();
        
        System.out.println("\n制作茶:");
        Beverage tea = new Tea();
        tea.prepareBeverage();
    }
}

抽象类的作用

  • 封装不变部分(烧水、装杯),避免子类重复编写;
  • 声明可变部分(冲泡、加调料)为抽象方法,强制子类实现;
  • 用final保护模板方法,防止子类篡改算法整体流程。

3.2 工厂方法模式

属于创建型模式,抽象类在这里扮演"抽象工厂"的角色,定义创建产品的接口,具体创建逻辑交给子类实现

核心思想:抽象工厂类声明一个创建产品的抽象方法,每个具体工厂子类负责创建对应的具体产品,解耦"产品创建"和"产品使用"。

》简单的用话来说就是:工厂=生产东西的地方;工厂方法=一个专门用来创建对象的方法。把创建对象的代码单独抽出来交给工厂做,你只管使用,不管怎么创建,也就是你要什么,跟工厂说,工厂给你造好,你直接用。就比如奶茶店:你去买奶茶,你不需要知道奶茶怎么煮,加多少奶加多少珍珠,你只需要和店员说我要珍珠奶茶,店员直接给你一杯做好的奶茶。这就是工厂方法模式,奶茶=对象,店员=工厂,你点单=调用工厂方法,你拿到奶茶=获取对象。

代码示例

java 复制代码
// 第一步:定义产品抽象类(所有日志记录器的通用规范)
public abstract class Logger {
    // 抽象方法:记录日志
    public abstract void log(String message);
}

// 具体产品1:文件日志记录器
public class FileLogger extends Logger {
    @Override
    public void log(String message) {
        System.out.println("写入文件:" + message);
    }
}

// 具体产品2:控制台日志记录器
public class ConsoleLogger extends Logger {
    @Override
    public void log(String message) {
        System.out.println("打印到控制台:" + message);
    }
}

// 第二步:定义抽象工厂类(创建日志记录器的工厂模板)
public abstract class LoggerFactory {
    // 抽象工厂方法:创建日志记录器(子类决定创建哪种)
    public abstract Logger createLogger();
    
    // 通用方法:获取日志器并记录(复用逻辑)
    public void recordLog(String message) {
        Logger logger = createLogger();
        logger.log(message);
    }
}

// 具体工厂1:创建文件日志器
public class FileLoggerFactory extends LoggerFactory {
    @Override
    public Logger createLogger() {
        // 可添加文件日志器的初始化逻辑(如创建文件、权限检查)
        return new FileLogger();
    }
}

// 具体工厂2:创建控制台日志器
public class ConsoleLoggerFactory extends LoggerFactory {
    @Override
    public Logger createLogger() {
        return new ConsoleLogger();
    }
}

// 测试
public class TestFactory {
    public static void main(String[] args) {
        LoggerFactory factory1 = new FileLoggerFactory();
        factory1.recordLog("用户登录成功"); // 输出:写入文件:用户登录成功
        
        LoggerFactory factory2 = new ConsoleLoggerFactory();
        factory2.recordLog("系统启动失败"); // 输出:打印到控制台:系统启动失败
    }
}

抽象类的作用

  • Logger(产品抽象类):规范所有日志记录器的核心行为(log方法);
  • LoggerFactory(工厂抽象类):定义创建产品的接口(createLogger),同时封装通用的日志记录逻辑(recordLog),实现代码复用。
相关推荐
困死,根本不会1 小时前
【C 语言】指针学习笔记:从底层原理到实战应用
c语言·开发语言·笔记·学习·算法
自动化和Linux2 小时前
C语言_scanf(),strlen(),size()的特性和各自的区别
c语言·开发语言
hx862272 小时前
Java MySQL 连接
java·mysql·adb
lpfasd1232 小时前
Kubernetes (K8s) 底层早已不再直接使用 Docker 引擎了
java·docker·kubernetes
aq55356002 小时前
SpringBoot有几种获取Request对象的方法
java·spring boot·后端
小郝 小郝2 小时前
51 与32 单片机LED控制详解
c语言·开发语言·经验分享·学习·51单片机
星空露珠2 小时前
迷你世界UGC3.0脚本Wiki全局函数
开发语言·数据库·算法·游戏·lua
金山几座2 小时前
C#学习记录-类(Class)
开发语言·学习·c#
AsDuang3 小时前
Python 3.12 MagicMethods - 55 - __irshift__
开发语言·python