Java学习笔记之抽象类

前言

在学习 Java 的过程中,当你觉得自己已经掌握了"类"和"继承"之后,abstract 这个关键字会突然冒出来,带来一个新概念------抽象类。它和普通类有什么区别?为什么要用它?什么时候该用它?这篇文章从普通类出发,讲清楚抽象类的来龙去脉。


一、概念:什么是抽象类

1.1 从一个问题开始

假设你要设计一个"动物"系统。狗、猫、鸟......它们都有名字、都会吃东西,但"叫"的方式各不相同。你写了一个普通类 Animal

java 复制代码
// 动物
public class Animal {

    private String name;

    // 构造方法
    public Animal(String name) {
        this.name = name;
    }

    // 问题:动物都会叫,但怎么叫?
    // 狗是"汪汪",猫是"喵喵",鱼......不出声
    // 在 Animal 这个层面,你没法给出一个通用的实现
    public void makeSound() {
        // 空实现?------子类忘记重写也不会报错
        // 抛异常?------运行时才发现问题
    }

    // 普通方法
    public String getName() {
        return name;
    }

    public void eat() {
        System.out.println(name + " is eating.");
    }
}

问题出在哪?

java 复制代码
Animal animal = new Animal("某动物"); // 能实例化!但"某动物"怎么叫?
animal.makeSound();                    // 空实现,什么也不做

// 如果某个子类忘记重写 makeSound(),编译器不会报错
// 只有运行时才发现"叫"没有效果

普通类有两个问题:

  1. 无法阻止实例化------"动物"是一个抽象概念,不应该被直接创建
  2. 无法强制子类实现某个方法------子类忘了重写,编译器也不报错

1.2 抽象类的定义

抽象类 就是在这两个问题的基础上诞生的。它在普通类前面加了 abstract 关键字,允许你定义没有方法体的方法(抽象方法),强制子类必须实现。

一句话理解:抽象类是一张"没画完的设计图"------总体框架有了,某些细节留给施工方去完成。

java 复制代码
// 加 abstract 关键字 → 变成抽象类
public abstract class Animal {

    private String name;

    // 构造方法:和普通类一样
    public Animal(String name) {
        this.name = name;
    }

    // 加 abstract 关键字 → 抽象方法(没有方法体)
    // 子类必须实现,否则编译报错!
    public abstract void makeSound();

    // 普通方法:和普通类一样,有方法体
    public String getName() {
        return name;
    }

    public void eat() {
        System.out.println(name + " is eating.");
    }
}

和普通类相比,只变了三点:

变化 普通类 抽象类
类声明 class Animal abstract class Animal(加了 abstract
方法 全部必须有方法体 可以有没有方法体的抽象方法
实例化 可以 new 不能 new

除此之外,抽象类能做的,普通类都能做。


二、性质:抽象类的完整特性

2.1 逐条说明

java 复制代码
public abstract class Animal {

    // 1. 成员变量:和普通类完全一样,任意类型和访问修饰符
    private String name;                // 私有变量
    protected int age;                  // 受保护变量
    public static final int MAX_AGE = 200; // 常量
    private static int count = 0;       // 静态变量

    // 2. 构造方法:和普通类完全一样,支持重载
    //    虽然不能直接 new,但子类会通过 super() 调用它
    public Animal(String name) {
        this.name = name;
        count++;
    }

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

    // 3. 抽象方法:普通类不能有,抽象类才能有
    //    没有方法体,子类必须实现(除非子类也是抽象类)
    public abstract void makeSound();

    // 4. 普通方法:和普通类完全一样
    public void eat() {
        System.out.println(name + " is eating.");
    }

    // 5. 静态方法:和普通类完全一样
    public static int getCount() {
        return count;
    }

    // 6. 可以继承另一个类(单继承)
    // 7. 可以实现接口(多实现)
    // 8. 可以有 final 方法(禁止子类重写)
    public final String getDescription() {
        return "This is " + name;
    }
}

2.2 特性对照表

性质 普通类 抽象类 变化说明
能否被实例化 不能 new Animal() 编译报错
能否有构造方法 不变------给子类通过 super() 调用
能否有成员变量 不变------任意类型和访问修饰符
能否有抽象方法 不能 新增能力------强制子类实现
能否有普通方法 不变
能否有静态方法 不变
能否有 final 方法 不变
继承父类 单继承 单继承 不变
实现接口 多实现 多实现 不变

可以看出,抽象类 = 普通类 + abstract 关键字 + 抽象方法 - 实例化能力。它是对普通类的小幅增强,而不是一个全新的概念。

2.3 抽象类的子类

java 复制代码
// 狗------继承 Animal,实现自己的叫声
public class Dog extends Animal {

    public Dog(String name) {
        super(name); // 调用抽象类的构造方法
    }

    @Override  // 表示"我正在重写父类的方法"------如果方法签名写错了,编译器会报错
    public void makeSound() {
        System.out.println(getName() + ": 汪汪汪!");
    }

    // eat() 继承自 Animal,可以直接使用,也可以选择重写
    @Override
    public void eat() {
        System.out.println(getName() + " is eating dog food.");
    }
}
java 复制代码
// 猫------继承 Animal,实现自己的叫声
public class Cat extends Animal {

    public Cat(String name) {
        super(name); // 调用抽象类的构造方法
    }

    @Override
    public void makeSound() {
        System.out.println(getName() + ": 喵喵喵!");
    }
}

使用:

java 复制代码
// Animal a = new Animal("xx");  // 编译错误!抽象类不能实例化
// 这正是我们想要的------"动物"是一个抽象概念,不应该被直接创建

Animal dog = new Dog("旺财");    // 多态:父类引用指向子类对象
dog.makeSound();                  // 旺财: 汪汪汪!
dog.eat();                        // 旺财 is eating dog food.

Animal cat = new Cat("咪咪");
cat.makeSound();                  // 咪咪: 喵喵喵!
cat.eat();                        // 咪咪 is eating.(未重写,用父类的)

// 如果某个子类忘记实现 makeSound(),编译器直接报错:
// "Dog is not abstract and does not override abstract method makeSound() in Animal"

什么是"多态"? "多态"就是"同一个指令,不同的行为"。上面 dog.makeSound() 输出"汪汪汪",cat.makeSound() 输出"喵喵喵"------同样是 makeSound(),不同对象有不同的表现。Java 通过"父类引用指向子类对象"(如 Animal dog = new Dog(...))来实现多态,调用方法时会自动执行子类重写的版本。

2.4 抽象类还可以继承抽象类

java 复制代码
// 顶层抽象类:定义所有动物的共同行为
public abstract class Animal {
    public abstract void makeSound();
}
java 复制代码
// 中间层抽象类:宠物,扩展了"主人"属性,可以不实现父类抽象方法
public abstract class Pet extends Animal {
    private String owner;

    public Pet(String owner) {
        this.owner = owner;
    }

    public String getOwner() {
        return owner;
    }

    // Pet 也可以不实现 makeSound(),继续留给下一层子类
    public abstract String getPetType();
}
java 复制代码
// 具体类:狗,必须实现所有继承链上的抽象方法
public class Dog extends Pet {

    public Dog(String owner) {
        super(owner);
    }

    @Override
    public void makeSound() {
        System.out.println("汪汪汪!");
    }

    @Override
    public String getPetType() {
        return "Dog";
    }
}

三、作用:为什么要用抽象类

抽象类的作用可以概括为三个词:约束、复用、模板

3.1 约束------强制子类实现

java 复制代码
// 没有 abstract:子类忘了重写也不报错,运行时才发现
public class Animal {
    public void makeSound() { }  // 空实现
}
java 复制代码
// 有 abstract:子类忘了实现,编译直接报错
public abstract class Animal {
    public abstract void makeSound();  // 没有实现,必须由子类提供
}

价值:把运行时错误提前到编译期发现。

3.2 复用------共享公共代码

java 复制代码
// 抽象类:共享公共属性和方法,子类只需实现差异部分
public abstract class Animal {

    private String name;

    public Animal(String name) { this.name = name; }

    // 所有子类共享这份代码,不用每个子类都写一遍
    public String getName() {
        return name;
    }

    public void eat() {
        System.out.println(name + " is eating.");
    }

    public void sleep() {
        System.out.println(name + " is sleeping.");
    }

    public abstract void makeSound();
}

3.3 模板------定义算法骨架

这是抽象类最核心的价值,详见下一节"经典场景"。

3.4 三大作用总结

作用 核心思想 解决的问题 关键字/机制
约束 强制子类实现 子类忘记重写,运行时才发现 abstract 方法
复用 共享公共代码 多个子类重复编写相同逻辑 普通方法 + 成员变量
模板 固定算法骨架,留出可变步骤 多个子类算法流程相同,仅步骤不同 final 模板方法 + abstract 步骤

三者的关系:

复制代码
约束(abstract 方法)
  ↓ 强制子类实现
复用(普通方法 + 成员变量)
  ↓ 子类共享公共代码
模板(final 方法调用 abstract 方法)
  ↓ 固定流程,变化点留给子类

一句话总结:抽象类用 abstract 方法约束 子类必须实现什么,用普通方法和成员变量复用 公共代码,用模板方法模式把约束和复用组合成固定的算法骨架------这就是抽象类的全部价值。


四、场景:抽象类的典型应用

4.1 模板方法模式(最核心的场景)

场景描述:多个子类有相同的算法流程,只是某些步骤不同。

java 复制代码
/**
 * 模板方法模式:制作饮料
 * 抽象类定义流程骨架(模板),子类实现具体步骤
 */
public abstract class CaffeineBeverage {

    /**
     * 模板方法:定义为 final,防止子类重写算法骨架
     * 流程固定:烧水 → 冲泡 → 倒杯 → 加调料
     * 其中"冲泡"和"加调料"留给子类实现
     */
    public final void prepareRecipe() {
        boilWater(); // 烧水
        brew(); // 冲泡
        pourInCup(); // 倒杯
        if (customerWantsCondiments()) {
            addCondiments(); // 加调料
        }
    }

    // 公共实现:所有子类共享
    private void boilWater() {
        System.out.println("烧开水");
    }

    private void pourInCup() {
        System.out.println("倒入杯中");
    }

    // 抽象方法:子类必须实现
    protected abstract void brew();
    protected abstract void addCondiments();

    // 钩子方法:子类可以选择重写,也可以使用默认实现
    protected boolean customerWantsCondiments() {
        return true;
    }
}

什么是"钩子方法"?

"钩子"就是预留的一个"挂钩"------默认什么都不做(或返回 true),子类如果有特殊需求就"挂"上去(重写),没有就用默认的。

它和抽象方法的区别:抽象方法必须重写,钩子方法可以选择重写。 上面的 customerWantsCondiments() 就是一个钩子------咖啡不重写它(默认加调料),茶重写了它(不加调料)。

java 复制代码
// 咖啡:冲泡咖啡粉,加糖和牛奶
public class Coffee extends CaffeineBeverage {

    @Override
    protected void brew() {
        System.out.println("用沸水冲泡咖啡粉");
    }

    @Override
    protected void addCondiments() {
        System.out.println("加糖和牛奶");
    }
}
java 复制代码
// 茶:浸泡茶叶,通过钩子方法选择不加调料
public class Tea extends CaffeineBeverage {

    @Override
    protected void brew() {
        System.out.println("用沸水浸泡茶叶");
    }

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

    // 重写钩子方法
    @Override
    protected boolean customerWantsCondiments() {
        return false;
    }
}

使用:

java 复制代码
CaffeineBeverage coffee = new Coffee();
coffee.prepareRecipe();
// 烧开水 → 用沸水冲泡咖啡粉 → 倒入杯中 → 加糖和牛奶

CaffeineBeverage tea = new Tea();
tea.prepareRecipe();
// 烧开水 → 用沸水浸泡茶叶 → 倒入杯中(不加调料)

设计思想 :模板方法用 final 锁住算法骨架,用 abstract 留出可变步骤,用钩子方法提供可选的扩展点。这就是"开闭原则"------对扩展开放(新增子类),对修改关闭(不改模板)。

4.2 骨架实现模式(为接口提供默认实现)

场景描述:接口定义了很多方法,但大多数实现类只需要关心其中几个核心方法。抽象类提供一个"骨架",把不关心的方法给出默认实现。

java 复制代码
// 接口:定义了 8 个方法
public interface Logger {
    void trace(String msg);
    void debug(String msg);
    void info(String msg);
    void warn(String msg);
    void error(String msg);
    void fatal(String msg);
    String getName();
    void close();
}
java 复制代码
// 抽象类:提供骨架实现,子类只需实现 1 个核心方法 doLog()
public abstract class AbstractLogger implements Logger {

    protected String name;
    protected int level; // 0=TRACE, 1=DEBUG, 2=INFO, 3=WARN, 4=ERROR, 5=FATAL

    public AbstractLogger(String name, int level) {
        this.name = name;
        this.level = level;
    }

    @Override
    public String getName() {
        return name;
    }

    // 所有日志级别都委托给同一个 doLog() 方法
    @Override
    public void trace(String msg) { if (level <= 0) doLog("TRACE", msg); }

    @Override
    public void debug(String msg) { if (level <= 1) doLog("DEBUG", msg); }

    @Override
    public void info(String msg)  { if (level <= 2) doLog("INFO", msg); }

    @Override
    public void warn(String msg)  { if (level <= 3) doLog("WARN", msg); }

    @Override
    public void error(String msg) { if (level <= 4) doLog("ERROR", msg); }

    @Override
    public void fatal(String msg) { if (level <= 5) doLog("FATAL", msg); }

    @Override
    public void close() { /* 默认什么都不做 */ }

    // 子类只需实现这个核心方法
    protected abstract void doLog(String level, String msg);
}
java 复制代码
// 子类:只关心"怎么输出",不用管日志级别判断
public class ConsoleLogger extends AbstractLogger {

    public ConsoleLogger(String name, int level) {
        super(name, level);
    }

    @Override
    protected void doLog(String level, String msg) {
        System.out.println("[" + level + "] " + name + " - " + msg);
    }
}
java 复制代码
// 文件日志:将日志写入指定文件
public class FileLogger extends AbstractLogger {

    private String filePath;

    public FileLogger(String name, int level, String filePath) {
        super(name, level);
        this.filePath = filePath;
    }

    @Override
    protected void doLog(String level, String msg) {
        System.out.println("写入 " + filePath + ": [" + level + "] " + msg);
    }
}

使用:

java 复制代码
Logger logger = new ConsoleLogger("APP", 2); // INFO 级别
logger.debug("不会输出");                      // 级别不够
logger.info("服务启动");                       // [INFO] APP - 服务启动
logger.error("连接超时");                      // [ERROR] APP - 连接超时

4.3 通用基类(共享状态和行为)

"状态"就是对象持有的数据,也就是成员变量(字段)。 比如 idcreatedAtupdatedAt就是状态------多个方法都要用它们,它们是对象内部共享的数据。

场景描述:多个类有相同的属性和方法,提取到抽象基类中。

java 复制代码
// 通用实体基类:封装所有实体的公共字段(id、创建时间、更新时间)和通用行为
public abstract class BaseEntity {

    private Long id;
    private LocalDateTime createdAt;   // LocalDateTime 是 Java 8+ 提供的日期时间类,now() 获取当前时间
    private LocalDateTime updatedAt;

    // 公共逻辑
    public boolean isNew() {
        return id == null;
    }

    public void prePersist() {
        this.createdAt = LocalDateTime.now();
        this.updatedAt = LocalDateTime.now();
    }

    public void preUpdate() {
        this.updatedAt = LocalDateTime.now();
    }

    // getter/setter ...
}
java 复制代码
// 用户实体:继承 BaseEntity,自动拥有 id、时间戳等公共字段
public class User extends BaseEntity {
    private String username;
    private String email;
}
java 复制代码
// 订单实体:继承 BaseEntity,自动拥有 id、时间戳等公共字段
public class Order extends BaseEntity {
    private BigDecimal amount;  // BigDecimal 是 Java 中表示精确小数的类,比 double 更适合表示金额
    private String status;
}

五、举例:完整的代码示例

5.1 图形面积计算

java 复制代码
// 图形抽象类:定义面积和周长的计算契约,所有图形共享颜色属性
public abstract class Shape {

    private String color;

    public Shape(String color) {
        this.color = color;
    }

    // 抽象方法:每种图形的面积计算方式不同
    public abstract double area();

    // 抽象方法:每种图形的周长计算方式不同
    public abstract double perimeter();

    // 普通方法:所有图形共享
    public String getColor() {
        return color;
    }

    // 普通方法:基于抽象方法 area() 的组合逻辑
    public void printInfo() {
        System.out.println("颜色:" + color
                + ",面积:" + String.format("%.2f", area())
                + ",周长:" + String.format("%.2f", perimeter()));
    }
}
java 复制代码
// 圆形:用半径计算面积(πr²)和周长(2πr)
public class Circle extends Shape {

    private double radius;

    public Circle(String color, double radius) {
        super(color);
        this.radius = radius;
    }

    @Override
    public double area() {
        return Math.PI * radius * radius;
    }

    @Override
    public double perimeter() {
        return 2 * Math.PI * radius;
    }
}
java 复制代码
// 矩形:用长宽计算面积(w×h)和周长(2(w+h))
public class Rectangle extends Shape {

    private double width;
    private double height;

    public Rectangle(String color, double width, double height) {
        super(color);
        this.width = width;
        this.height = height;
    }

    @Override
    public double area() {
        return width * height;
    }

    @Override
    public double perimeter() {
        return 2 * (width + height);
    }
}
java 复制代码
// 三角形:用海伦公式计算面积,三条边之和为周长
public class Triangle extends Shape {

    private double a, b, c;

    public Triangle(String color, double a, double b, double c) {
        super(color);
        this.a = a;
        this.b = b;
        this.c = c;
    }

    @Override
    public double area() {
        double s = (a + b + c) / 2;
        return Math.sqrt(s * (s - a) * (s - b) * (s - c));
    }

    @Override
    public double perimeter() {
        return a + b + c;
    }
}

使用:

java 复制代码
Shape[] shapes = {
    new Circle("红色", 5),
    new Rectangle("蓝色", 4, 6),
    new Triangle("绿色", 3, 4, 5)
};

for (Shape shape : shapes) {
    shape.printInfo();
}
// 颜色:红色,面积:78.54,周长:31.42
// 颜色:蓝色,面积:24.00,周长:20.00
// 颜色:绿色,面积:6.00,周长:12.00

5.2 员工工资计算

java 复制代码
// 员工抽象类:不同类型员工的工资计算方式不同,getDetails() 复用抽象方法
public abstract class Employee {

    private String name;
    private int id;

    public Employee(String name, int id) {
        this.name = name;
        this.id = id;
    }

    // 抽象方法:不同类型员工的工资计算方式不同
    public abstract double calculateSalary();

    // 普通方法
    public String getDetails() {
        return "ID: " + id + ", 姓名: " + name
                + ", 工资: " + String.format("%.2f", calculateSalary());
    }

    public String getName() { return name; }
    public int getId() { return id; }
}
java 复制代码
// 全职员工:固定月薪,工资 = monthlySalary
public class FullTimeEmployee extends Employee {

    private double monthlySalary;

    public FullTimeEmployee(String name, int id, double monthlySalary) {
        super(name, id);
        this.monthlySalary = monthlySalary;
    }

    @Override
    public double calculateSalary() {
        return monthlySalary;
    }
}
java 复制代码
// 兼职员工:按小时计薪,工资 = 时薪 × 工时
public class PartTimeEmployee extends Employee {

    private double hourlyRate;
    private int hoursWorked;

    public PartTimeEmployee(String name, int id, double hourlyRate, int hoursWorked) {
        super(name, id);
        this.hourlyRate = hourlyRate;
        this.hoursWorked = hoursWorked;
    }

    @Override
    public double calculateSalary() {
        return hourlyRate * hoursWorked;
    }
}
java 复制代码
// 提成员工:底薪 + 销售额 × 提成比例
public class CommissionEmployee extends Employee {

    private double baseSalary;
    private double salesAmount;
    private double commissionRate;

    public CommissionEmployee(String name, int id, double baseSalary,
                               double salesAmount, double commissionRate) {
        super(name, id);
        this.baseSalary = baseSalary;
        this.salesAmount = salesAmount;
        this.commissionRate = commissionRate;
    }

    @Override
    public double calculateSalary() {
        return baseSalary + salesAmount * commissionRate;
    }
}

使用:

java 复制代码
List<Employee> employees = List.of(
    new FullTimeEmployee("张三", 1, 15000),
    new PartTimeEmployee("李四", 2, 50, 80),
    new CommissionEmployee("王五", 3, 5000, 100000, 0.05)
);

for (Employee e : employees) {
    System.out.println(e.getDetails());
}
// ID: 1, 姓名: 张三, 工资: 15000.00
// ID: 2, 姓名: 李四, 工资: 4000.00
// ID: 3, 姓名: 王五, 工资: 10000.00

六、反例:抽象类的常见误用

6.1 只有抽象方法、没有状态的抽象类

java 复制代码
// 反例:抽象类里只有抽象方法,没有任何成员变量和具体实现
// 这意味着抽象类的"复用"和"模板"两大作用都没用上
public abstract class Flyable {
    public abstract void fly();
}
java 复制代码
// 正确做法:加上共享状态或具体方法,发挥抽象类的价值
public abstract class Flyable {

    private int altitude; // 共享状态:当前飞行高度

    public Flyable(int altitude) {
        this.altitude = altitude;
    }

    public abstract void fly();

    // 具体方法:复用逻辑
    public void checkAltitude() {
        if (altitude > 10000) {
            System.out.println("高度过高,请注意安全");
        }
    }
}

判断标准:如果一个抽象类里没有成员变量、没有具体方法,说明"复用"和"模板"的价值都没有发挥出来,需要重新审视是否有必要使用抽象类,可能使用接口更规范。

6.2 继承层次过深

java 复制代码
// 反例:5 层继承,越来越难维护
public abstract class Vehicle { }
public abstract class MotorVehicle extends Vehicle { }
public abstract class FourWheelVehicle extends MotorVehicle { }
public abstract class Car extends FourWheelVehicle { }
public abstract class SUV extends Car { }
public class TeslaModelY extends SUV { }
java 复制代码
// 正确做法:扁平化继承层次,或用组合代替继承
public abstract class Vehicle { }
public class TeslaModelY extends Vehicle { }

判断标准:继承层次超过 3 层就要警惕。考虑用组合(has-a)代替继承(is-a)。

6.3 抽象类承担过多职责

java 复制代码
// 反例:上帝类,什么都往里塞
public abstract class BaseEntity {
    public abstract void validate();
    public abstract void save();
    public abstract void sendNotification();
    public String toJson() { return "{}"; }
}
java 复制代码
// 正确做法:单一职责,抽象类只做一件事
public abstract class BaseEntity {
    private Long id;
    public abstract void validate();  // 只负责验证
    // save、toJson 等职责交给其他类
}

6.4 为了用抽象类而用抽象类

java 复制代码
// 反例:只有一个子类,没有必要用抽象类
public abstract class Animal {
    public abstract void makeSound();
}
java 复制代码
public class Dog extends Animal {
    @Override
    public void makeSound() { System.out.println("汪"); }
}
// 只有一个 Dog,没有 Cat、Bird......抽象类毫无意义

// 正确做法:至少有两个子类时,才考虑提取抽象类

七、速查清单

问题 答案
抽象类用什么修饰? abstract class
抽象类能实例化吗? 不能
抽象类能有构造方法吗? 能,给子类通过 super() 调用
抽象类能有成员变量吗? 能,和普通类一样
抽象类能有普通方法吗? 能,和普通类一样
抽象类能有静态方法吗? 能,和普通类一样
一个抽象方法都没有的类可以是抽象类吗? 可以
子类必须实现所有抽象方法吗? 是,否则子类也要声明为 abstract
抽象类能实现接口吗?
抽象类能继承抽象类吗?
什么时候用抽象类? 多个子类有相同属性/行为 + 需要强制子类实现某些方法
典型设计模式? 模板方法模式

八、面试口述:什么是抽象类

抽象类就是用 abstract 关键字修饰的类,它和普通类几乎一样,能拥有构造方法、成员变量、普通方法和静态方法,唯一的区别是:抽象类可以定义没有方法体的抽象方法,并且自身不能被实例化。子类继承抽象类后,必须实现所有抽象方法,否则子类自己也要声明为抽象类。这样就把运行时才能发现的问题提前到了编译期。

抽象类有三个核心作用:约束、复用、模板。约束是指用抽象方法强制子类必须实现某些行为;复用是指把公共的属性和方法放在抽象类中,避免子类重复编写;模板是指用 final 方法定义算法骨架,把可变的步骤声明为抽象方法让子类实现------这就是模板方法模式,也是抽象类最典型的应用场景。

需要注意的是,抽象类的构造方法虽然不能直接用来 new 对象,但子类会通过 super() 调用它,所以常用来做初始化和参数校验。另外,即使一个抽象方法都没有,类也可以声明为 abstract,纯粹用来禁止实例化。

相关推荐
海盗12341 小时前
C#中PDF操作-QuestPDF页面设置与布局
java·pdf·c#
day day day ...1 小时前
MyBatis / MyBatis-Plus 动态 SQL 中 OGNL 表达式的常见陷阱与源码分析
java·开发语言·mybatis
Kiling_07041 小时前
Java IO流:字节流实战与性能优化
java·开发语言·php
Esaka_Forever1 小时前
few‑shot learning(少样本学习)
人工智能·学习
January12071 小时前
IDEA 快捷键
java·ide·intellij-idea
周杰伦fans2 小时前
C# 异常继承深度解析:从设计原则到 sealed 关键字的奥秘
java·jvm·c#
搬石头的马农2 小时前
从零配置Claude自动修Bug:6步打造全自动开发流程
java·人工智能·python·bug·ai编程
小马爱打代码2 小时前
Redis Key 过期后会立刻删除吗?过期删除与内存淘汰策略详解
java·redis·缓存
鱼鳞_2 小时前
苍穹外卖-Day10(Spring task)
java·后端·spring