惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
博客园 - 司徒正美
Last Week in AI
Last Week in AI
博客园 - 聂微东
Jina AI
Jina AI
月光博客
月光博客
爱范儿
爱范儿
美团技术团队
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Hugging Face - Blog
Hugging Face - Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 叶小钗
T
Tailwind CSS Blog
博客园 - 【当耐特】
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Apple Machine Learning Research
Apple Machine Learning Research
有赞技术团队
有赞技术团队
罗磊的独立博客
小众软件
小众软件
雷峰网
雷峰网
IT之家
IT之家
大猫的无限游戏
大猫的无限游戏
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
V
Visual Studio Blog

Qubik的小站

Building an Interpreter: Introduction and Lexer 在 Rocky Linux 10 Workstation KDE 上安装中文输入法 2025 上半年记 | Qubik的小站 Rust 写脚本?可以的!(上) | Qubik的小站 2024 年终总结 | Qubik的小站 Racket 入门 - 模块与测试 | Qubik的小站 Racket 入门 - 条件表达式、递归与复合数据 | Qubik的小站 Racket 入门 - 配置与基础 | Qubik的小站 2024 年上半年总结 | Qubik的小站 Java 中的 Lambda 表达式 | Qubik的小站 大 O 表示法 | Qubik的小站 递归与归并排序 | Qubik的小站 基础的排序与搜索算法 | Qubik的小站 Java 中的泛型 | Qubik的小站 数据结构:链表(LinkedList) | Qubik的小站 数据结构:栈与队列 | Qubik的小站 异常处理 (Exception Handling) - 第一部分 Java 多态 (polymorphism) - 继承与抽象类/接口 二维数组、类类型数组与 ArrayList | Qubik's Site [LCTT 翻译转载] 软件开发|Git 提交是差异、快照还是历史记录? | Qubik's Site 2024 年 1 月总结 | Qubik's Site 2023 年 12 月总结 | Qubik's Site 2023 年 11 月总结 | Qubik's Site 2023 年 9、10 月总结 | Qubik's Site 创建使用 Windows 11 的(移除硬件限制的) Winddows To Go 系统 Minecraft 生存实用 Fabric 模组列表 | Qubik's Site 2023 年 6 至 8 月总结 [LCTT 翻译转载] 软件开发|Rust 基础系列 #8:编写里程碑 Rust 程序 [LCTT 翻译转载] 软件开发|Rust 基础系列 #7: 在 Rust 中使用循环 [LCTT 翻译转载] 软件开发|Rust 基础系列 #6: 条件语句
异常处理 (Exception Handling) - 第二部分
2024-03-02 · via Qubik的小站

00

在上一篇文章中,我们简单的讲述了异常处理的基本概念以及 try-catch, throw, throws 等关键机制。在本篇文章中,我们将继续讨论异常处理的两个高级特性:自定义异常类以及 catch 多个异常。

自定义异常类 ​

正如我们在第一部分中提到的,有些时候我们需要在 Java 不认为有异常情况的时候依照程序设计主动抛出异常。虽然我们有的时候是可以直接使用已有的异常类的,但是由于我们通常希望异常类本身的名字就能够为异常提供一定的描述,因此我们通常会自定义异常类。

java

java

public class Main {
    private static void checkGrade(int grade) {
        if (grade < 60) {
            throw new ArithmeticException("This grade is below passing grade!");
        }
    }

    public static void main(String[] args) {
        int grade = 50;
        try {
            checkGrade(grade);
        } catch (ArithmeticException arithmeticException) {
            System.out.println(arithmeticException);
        }
    }
}

这是上次我们使用过的示例,我们可以看到,在检查成绩的时候,我们直接使用了 ArithmeticException 这个异常类。但是数学错误其实并非我们真正想要表达的意思,我们更希望能够使用一个更加贴切的异常类名字。因此,我们可以自定义一个异常类:

java

java

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

public class Main {
    private static void checkGrade(int grade) throws GradeException {
        if (grade < 60) {
            throw new GradeException("This grade is below passing grade!");
        }
    }

    public static void main(String[] args) {
        int grade = 50;
        try {
            checkGrade(grade);
        } catch (GradeException gradeException) {
            System.out.println(gradeException);
        }
    }
}

这样,checkGrade 函数就不再会抛出 ArithmeticException,而是抛出我们自定义的 GradeException。这样,我们的异常处理就变得更加清晰了。你如果运行这段代码,你会发现输出的异常信息中包含的异常名也变成了 GradeException

对于自定义异常类,其实它们就是继承自 Exception 类或者 RuntimeException 类的类。一般我们会选择继承 Exception 类使得我们的自定义异常成为一个检查异常 (因为我们通常希望会在某处处理我们自定义的异常)。

一个基础的自定义异常长这样:

java

java

class CustomException extends Exception {
}

抛出这个自定义异常只需要:

java

java

throw new CustomException();

注意,该函数中没有任何的带参构造函数,所以此处隐性的使用了默认的无参构造函数。这样的错误在被打印出来的时候只会显示异常名字,而不会显示任何的异常信息。它的输出长这样:

shell

shell

CustomException

Process finished with exit code 0

你会发现输出里只有异常类名,任何关于异常的细节都没被提供。如果我们希望在抛出异常的时候能够提供一些额外的信息,我们可以使用 toString 方法:

java

java

class CustomException extends Exception {
    public String toString() {
        return "错误信息";
    }
}

不要这么做!!!不要这么做!!!不要这么做!!!

虽然任何显示错误信息的时候其实都是在调用 Exception 类的 toString 方法,而且重写 toString 方法可以使代码正常工作,但是这样做是不标准的。你应该在上面的代码中发现了,ArithmeticException 之类的异常类可以接受一个 String 类型的参数。没错,异常类有数个带参构造函数,这里我们只讨论最重要的一个,带有参数 String message 参数的构造函数。正如参数名所示,这个参数就是用来传递异常信息的。使用这个构造函数在打印异常信息的时候会显示这个参数的值。因此,我们应该使用这个构造函数来传递异常信息:

java

java

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

然后在抛出异常的时候使用:

java

java

throw new CustomException("错误信息");

输出就会成为:

shell

shell

CustomException: 错误信息

Process finished with exit code 0

如果你希望为自定义异常类添加一个默认的错误,你可以自定义无参构造函数,然后让它去调用父类 Exception 的带参构造函数:

java

java

class CustomException extends Exception {
    public CustomException() {
        super("默认错误信息");
    }
}

这样就算你在抛出异常的时候没有提供任何的参数,异常信息也会显示为 "默认错误信息"。

这个默认信息的示例代码如下:

java

java

class CustomException extends Exception {
    public CustomException() {
        super("默认错误信息");
    }
}

public class Main {
    private static void throwException() throws CustomException {
        throw new CustomException();
    }

    public static void main(String[] args) {
        try {
            throwException();
        } catch (CustomException customException) {
            System.out.println(customException);
        }
    }
}

输出为:

shell

shell

CustomException: 默认错误信息

Process finished with exit code 0

catch 多个异常 ​

有的时候我们可能希望一个 catch 可以捕获多个异常,然后对这些不同的异常作出相同的处理,这个时候我们只需要在 catch 的括号中使用 | 分隔不同的异常类名即可:

java

java

public class Main {
    public static void main(String[] args) {
        try {
            int[] arr = new int[5];
            int element = arr[5];
            int result = 5 / 0;
        } catch (ArrayIndexOutOfBoundsException | ArithmeticException e) {
            System.out.println(e);
        }
    }
}

在上述的代码中,同一个 catch 在数组下标越界异常和除零异常发生的时候都会被执行,且对这两个异常的处理都是一样的。这样的写法可以使得代码更加简洁,而且仍然允许我们不去使用 Exception 类的父类来捕获所有的异常。