Menu
CoddyTech

自定义异常

本课属于 Coddy Java 学习旅程的“面向对象编程”单元。第 60 课,共 87 课。

Java 内置的异常涵盖了许多常见的错误场景,但有时你需要创建适用于应用领域的特定异常。自定义异常可以让你创建有明确含义的错误类型,清楚地说明出了什么问题。

要创建自定义异常,只需扩展现有的异常类。对于已检查异常,扩展 Exception:

public class InsufficientFundsException extends Exception {
    public InsufficientFundsException(String message) {
        super(message);
    }
}

对于未经检查的异常,继承 RuntimeException:

public class InvalidAgeException extends RuntimeException {
    public InvalidAgeException(String message) {
        super(message);
    }
}

super(message) 调用会将错误消息传递给父类,因此可以通过 getMessage() 获取该消息。然后,你可以像处理其他异常一样,抛出并捕获自定义异常:

public void withdraw(double amount) throws InsufficientFundsException {
    if (amount > balance) {
        throw new InsufficientFundsException("Balance too low: " + balance);
    }
    balance -= amount;
}

// 使用它
try {
    account.withdraw(1000);
} catch (InsufficientFundsException e) {
    System.out.println(e.getMessage());
}

自定义异常能让代码更易读,也能让调用者针对特定错误情况采取不同的处理方式。在构建 API 或库时,清晰地传达错误尤其重要,因此自定义异常也特别有用。

challenge icon

挑战

简单

让我们构建一个使用自定义异常处理不同错误情况的票务预订系统!你将创建有意义的异常类型,清楚地说明预订门票时出了什么问题。

你将把代码分成 4 个文件:

  • TicketSoldOutException.java:创建一个自定义受检异常(继承 Exception),用于表示门票已售罄。异常的构造函数应接收一条消息,并使用 super(message) 将消息传递给父类。
  • InvalidSeatException.java:创建一个自定义非受检异常(继承 RuntimeException),用于表示有人尝试预订无效的座位号。这个异常也应接收一条消息,并将其传递给父类。
  • TicketBooth.java:创建一个管理门票销售的类。售票处应记录可用座位总数和有效座位号的最大值。包括:

    一个接收可用座位总数的构造函数。

    一个用于验证预订的 bookTicket(int seatNumber) 方法。如果没有剩余门票(可用座位数小于或等于 0),则抛出消息为 "No tickets remaining" 的 TicketSoldOutException。如果座位号小于 1 或大于座位总数,则抛出消息为 "Seat [seatNumber] does not exist" 的 InvalidSeatException。否则,将可用座位数减 1,并输出 Booked seat [seatNumber] successfully。

    一个返回当前可用座位数的 getAvailableSeats() 方法。

  • Main.java:把票务系统组合起来!你会收到两个输入:座位总数和要预订的座位号。

    使用给定的座位总数创建一个 TicketBooth。然后在 try-catch 代码块中尝试预订指定座位号。由于 TicketSoldOutException 是受检异常,你必须处理它。先捕获 TicketSoldOutException,并输出 Booking failed: [exception message]。然后捕获 InvalidSeatException,并以相同格式输出:Booking failed: [exception message]。

    在 try-catch 代码块之后,使用 getter 方法输出 Remaining seats: [count]。

你会按顺序收到两个输入:座位总数(整数)和要预订的座位号(整数)。

注意,受检异常会要求调用方处理它们(使用 try-catch 或 throws),而非受检异常不需要显式处理。请根据错误是否属于调用方应该预料到并进行恢复的情况,选择异常类型!

亲手试试

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int totalSeats = scanner.nextInt();
        int seatNumber = scanner.nextInt();
        
        // TODO: 根据给定的座位总数创建 TicketBooth
        
        // TODO: 使用 try-catch 块尝试预订座位
        // 先捕获 TicketSoldOutException,并打印 "Booking failed: [exception message]"
        // 捕获 InvalidSeatException,并打印 "Booking failed: [exception message]"
        
        // TODO: 在 try-catch 块之后,打印 "Remaining seats: [count]"
    }
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Java 在线编译器