目录
1. 异常的介绍
在编程中,异常和错误是两种重要的运行时问题,它们代表了程序在执行过程中出现的异常情况。理解它们的区别和如何处理异常是编写健壮程序的基础。
1.1 错误(Error)
**错误(Error)**表示一些严重的、通常无法恢复的程序问题。错误通常是由于虚拟机(JVM)等底层环境的内部故障引起的,程序员无法捕捉或处理这些错误。
错误的特点:
- 不常见:错误通常发生在程序执行的基础环境中,应用程序本身无法直接引起。
- 无法解决:一旦出现错误,程序通常无法继续执行。解决错误通常意味着修改系统配置或更换硬件,而非通过代码修改。
- 尽量避免:错误通常不可预见且无法避免,所以在实际开发中我们尽量避免会导致错误的操作或环境配置问题。
常见的错误示例:
OutOfMemoryError:表示JVM内存不足,无法分配内存。StackOverflowError:表示程序调用栈溢出,通常是由于递归调用过深造成的。
// OutOfMemoryError示例
public class ErrorExample {
public static void main(String[] args) {
// 大量分配内存,模拟内存溢出错误
try {
int[] arr = new int[Integer.MAX_VALUE];
} catch (Error e) {
System.out.println("Error: " + e.getMessage());
}
}
}
1.2 异常(Exception)
**异常(Exception)**表示程序在执行时出现的意外情况,可以通过代码捕捉并进行处理。异常是程序可以预见并且通过适当的手段进行修复的。
异常的特点:
- 常见:异常发生的频率要远高于错误,通常是由于程序中的输入错误、资源不可用、网络中断等原因造成的。
- 可解决:与错误不同,异常可以通过捕捉和处理来解决。例如,捕获网络连接失败异常并重新尝试连接,或者捕获用户输入错误并提示用户重新输入。
- 代码语法无问题:当程序发生异常时,通常是运行时出现的问题,而不是代码语法错误。
异常的分类:
- 检查型异常(Checked Exception):这类异常是编译器强制要求进行处理的。如果方法中可能抛出某种检查型异常,必须通过
try-catch语句或throws声明来处理或传递该异常。常见的检查型异常有IOException、SQLException等。 - 非检查型异常(Unchecked Exception):这类异常在编译时不要求进行处理,它们通常是程序的逻辑错误,发生时无法预见到。非检查型异常的根类是
RuntimeException。常见的非检查型异常有NullPointerException、ArrayIndexOutOfBoundsException、ArithmeticException等。
// 处理异常示例
public class ExceptionExample {
public static void main(String[] args) {
try {
// 假设除以0抛出异常
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("异常: " + e.getMessage());
} finally {
System.out.println("无论是否发生异常,finally块都会执行");
}
}
}
总结:
- 错误通常是由系统环境问题引起,无法通过代码解决,程序员无法捕获或处理它们。
- 异常则是程序中常见的问题,可以通过捕捉和处理来使程序继续运行,代码通常会明确声明哪些异常可能发生,并做好处理。
异常处理机制是Java中处理程序运行时问题的核心,学习如何有效地捕捉和处理异常是开发稳定程序的重要步骤。
2. 异常举例以及解决常见错误bug方案
在程序开发中,我们难免会遇到一些错误和异常,尤其是在编写复杂功能时。学习如何定位错误、理解异常信息、以及如何解决常见的错误,对于提高调试和问题解决的效率至关重要。
2.1 定位错误
定位错误时,最重要的是理解异常提示信息。Java的异常信息通常会提供足够的细节来帮助我们定位问题。异常信息包括错误类型、错误发生的位置(代码行号)、以及堆栈跟踪。
步骤:
- 运行程序,查看控制台中出现的异常提示。
- 异常信息会提供错误的类型(如
ArithmeticException、ArrayIndexOutOfBoundsException)以及发生错误的代码行。 - 如果异常信息足够明确,可以直接根据错误提示修改代码。
- 如果不理解异常信息,可以搜索错误类型,通常可以在StackOverflow或者Java文档中找到相关解决方案。
2.2 常见错误及解决方案
在以下代码示例中,我们可以看到两种常见的错误,并提供了如何通过异常信息定位和解决这些错误的方法。
package com.nix.demo;
import org.junit.Test;
// 2. 异常举例以及解决常见错误bug方案
// 定位错误:编写好程序后,运行程序,在输入运行结果栏中会存在异常提示,红色中蓝色链接,就能找到自己的代码错误
//解决错误:先阅读异常提示,如果了解就直接修改,如果不了解那就搜索异常提示,了解问题所在,解决问题
public class Main {
@Test
public void demo() {
// ArithmeticException: / by zero (除数为零)
System.out.println(1 / 0); // 此处会抛出异常:ArithmeticException
}
@Test
public void demo2() {
int[] a = {1, 2, 3, 4};
// ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 4 (索引越界)
System.out.println(a[5]); // 此处会抛出异常:ArrayIndexOutOfBoundsException
}
}
2.3 错误举例
-
除零异常 (
ArithmeticException)public void demo() { System.out.println(1 / 0); }异常信息:
java.lang.ArithmeticException: / by zero解决方案:
- 这是由于尝试将一个数除以0导致的错误。解决方法是检查除数是否为0,并在除法操作前进行适当的验证。
public void demo() { int divisor = 0; if (divisor != 0) { System.out.println(1 / divisor); } else { System.out.println("除数不能为零"); } } -
数组越界异常 (
ArrayIndexOutOfBoundsException)public void demo2() { int[] a = {1, 2, 3, 4}; System.out.println(a[5]); }异常信息:
java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 4解决方案:
- 这表示你尝试访问数组中不存在的索引。数组
a的长度为4,而你尝试访问索引5。解决方法是检查数组的长度并确保索引在有效范围内。
public void demo2() { int[] a = {1, 2, 3, 4}; int index = 5; if (index < a.length) { System.out.println(a[index]); } else { System.out.println("数组索引越界"); } } - 这表示你尝试访问数组中不存在的索引。数组
2.4 处理异常
为了提高程序的健壮性,避免程序崩溃,可以使用try-catch语句来捕捉并处理异常。
例如,对于除零异常,我们可以使用try-catch块来捕捉并处理:
public void demo() {
try {
System.out.println(1 / 0);
} catch (ArithmeticException e) {
System.out.println("捕获到异常: " + e.getMessage());
}
}
对于数组越界异常,我们也可以用类似的方式处理:
public void demo2() {
int[] a = {1, 2, 3, 4};
try {
System.out.println(a[5]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组索引越界: " + e.getMessage());
}
}
总结
- 定位错误:阅读异常提示,定位错误发生的位置。错误信息中通常会包含错误类型、行号和堆栈信息,帮助我们理解问题。
- 解决错误:根据异常信息判断问题所在,并采取措施解决。常见的错误如除零异常和数组越界异常,可以通过添加检查逻辑来解决。
- 异常处理:使用
try-catch语句捕捉异常,避免程序崩溃,并提供用户友好的错误信息。
3. RuntimeException 解析
在 Java 中,异常被划分为两大类:运行时异常(RuntimeException)**和**非运行时异常(checked exceptions)。这两类异常的处理方式和影响有所不同,下面我们将详细分析 RuntimeException 以及它与非运行时异常的区别。
3.1 运行时异常(RuntimeException)
RuntimeException 属于 未检查异常(Unchecked Exception),在编译时,Java 不强制要求捕获这种类型的异常。也就是说,程序员可以选择是否在代码中捕获这种异常。
特点:
- 不会强制要求捕获:编译器不会强制要求我们用
try-catch或者throws来处理RuntimeException,因为它是一个运行时异常。 - 不影响项目运行:虽然
RuntimeException会导致程序在某个时刻出现问题,但它不会影响程序的编译和执行过程,通常是逻辑错误或者不符合预期的运行状态。 - 程序员可以修改或不修改:
RuntimeException不必在代码中处理,但如果有可能会触发这种异常,最好进行防范处理。
常见的 RuntimeException 类型包括:
ArithmeticException(如除以零)NullPointerException(空指针异常)IndexOutOfBoundsException(数组或列表索引越界)IllegalArgumentException(非法参数异常)
3.2 非运行时异常(Checked Exception)
与 RuntimeException 不同,非运行时异常是需要进行处理的,Java 强制要求捕获或者声明(通过 throws)这种异常。
特点:
- 必须处理:非运行时异常会强制要求你进行捕获(
try-catch)或者声明(throws)。 - 影响程序执行:这种异常通常会导致程序无法继续执行,必须进行合理的异常处理,或者通过预防措施避免出现异常。
常见的非运行时异常:
IOException(输入输出异常)SQLException(SQL 异常)FileNotFoundException(文件未找到异常)
3.3 代码示例
以下是一些常见的 RuntimeException 示例:
package com.nix.demo;
import org.junit.Test;
// 3. RuntimeException
/*
异常类有两个主要的子类:IOException 类和 RuntimeException 类
1.运行时异常 RuntimeException
运行时异常可修改也可不修改,不会对项目运行产生影响
运行时异常像游戏漏洞,它不影响我们玩游戏,但是我们有一些漏洞可以捡,比如更新以后某个英雄的技能,在某个时间可以无限的放或者平A就能秒死人
2.非运行时异常
非运行时异常必须修改,因为这样会使得项目直接无法运行(现在的编译器比较智能,一定会让你try catch)
但是非运行时异常导致你进不去游戏
编译器中,运行时异常不会要求你捕获,但是非运行时异常会强制要求你捕获,所以我们在编写自定义异常的
时候不会定成运行时异常
*/
public class Main {
@Test
public void demo1() {
// 运行时异常: 除零异常
System.out.println(1 / 0); // 会抛出 ArithmeticException
}
@Test
public void demo2() {
try {
// 运行时异常: 类未找到
Class.forName("test321.hello"); // 会抛出 ClassNotFoundException
} catch (ClassNotFoundException e) {
System.out.println("ClassNotFoundException: " + e.getMessage());
}
}
}
3.4 异常说明
-
demo1示例:System.out.println(1 / 0);该代码会抛出一个
ArithmeticException,因为除以零在数学上是没有定义的,因此 Java 会抛出一个运行时异常ArithmeticException。这是一种运行时异常,程序可以选择不处理,但最好加以预防。 -
demo2示例:Class.forName("test321.hello");该代码会抛出一个
ClassNotFoundException,因为指定的类test321.hello并不存在。ClassNotFoundException是一个检查型异常,Java 编译器会强制要求你捕获它或声明抛出该异常。尽管ClassNotFoundException不是RuntimeException,它依然需要处理。
3.5 运行时异常的处理
虽然 RuntimeException 不需要强制捕获,但在实际开发中,我们通常会根据需求对运行时异常进行适当的处理。例如:
- 在输入数据时检查有效性,避免空指针异常。
- 在除法运算中检查除数是否为零,避免算术异常。
- 在访问数组或列表时检查索引是否越界。
示例: 处理 ArithmeticException 和 NullPointerException:
public void safeDivide(int a, int b) {
try {
if (b == 0) {
System.out.println("除数不能为零");
} else {
System.out.println(a / b);
}
} catch (ArithmeticException e) {
System.out.println("发生除零错误");
}
}
public void safeAccessArray(int[] arr, int index) {
try {
System.out.println(arr[index]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组索引越界");
}
}
3.6 总结
- 运行时异常(
RuntimeException):无需强制处理,可以选择捕获或不捕获,但应在可能发生异常的地方加入预防措施。 - 非运行时异常(Checked Exception):需要强制捕获或声明,程序员必须处理这些异常,否则编译无法通过。
- 通过合理的异常处理和预防措施,我们能够提高程序的健壮性,避免程序崩溃。
4. try-catch 异常捕获与处理
在 Java 中,try-catch 语句是用于捕获并处理异常的一种方式。它可以帮助我们有效地管理程序中可能出现的错误或异常情况,确保程序能够继续执行,而不会因为错误导致整个应用崩溃。
4.1 try-catch 语句的基本结构
try-catch 语句的基本语法如下:
try {
// 可能抛出异常的代码块
} catch (ExceptionType1 e1) {
// 捕获并处理ExceptionType1类型的异常
} catch (ExceptionType2 e2) {
// 捕获并处理ExceptionType2类型的异常
} finally {
// 最终执行的代码块,无论是否发生异常都会执行
}
try:在try块中写可能会抛出异常的代码。如果代码中发生了异常,控制权就会转到后续的catch块。catch:用来捕获并处理指定类型的异常。如果try块中的代码抛出了该类型的异常,那么相应的catch块就会执行。finally:finally块中的代码无论是否发生异常都会执行。它通常用于释放资源(如关闭文件流、数据库连接等)。
4.2 代码示例与解释
在下面的例子中,我们使用 try-catch 语句捕获 ArrayIndexOutOfBoundsException 异常,并在 finally 块中输出“异常捕获完成!”:
package com.nix.demo;
import org.junit.Test;
public class Main {
@Test
public void demo() {
// 在try块中,程序会执行代码,如果出现异常,它会跳到对应的catch块来处理。
try {
int[] a = new int[2]; // 创建长度为2的数组
System.out.println("Access element three :" + a[3]); // 尝试访问超出数组范围的元素
System.out.println("Out of the block");
} catch (Exception e) {
// 捕获并处理异常
// 可以选择输出异常信息,方便调试
// e.printStackTrace();
System.out.println("index越界!");
} finally {
// finally 块中的代码总会执行
System.out.println("异常捕获完成!");
}
}
}
4.3 运行结果
当程序执行时,首先会进入 try 块,尝试访问数组 a[3]。由于数组的最大索引为 1,a[3] 会引发 ArrayIndexOutOfBoundsException 异常。异常被 catch 块捕获并输出 "index越界!",然后程序执行 finally 块,输出 "异常捕获完成!"。
输出:
index越界!
异常捕获完成!
4.4 finally 的作用
- 确保执行:无论
try块中是否发生异常,finally中的代码总是会被执行。这对于需要关闭资源(如文件流、数据库连接等)非常有用。 - 强制执行:即使
try-catch语句块内发生了return语句,finally块的代码仍然会执行。
4.5 注意事项
finally块的执行顺序:即使try块内出现了return语句,finally块的代码也会在返回前执行。因此,finally常用于关闭资源,如文件流、数据库连接等。- 滥用
try-catch:虽然try-catch可以帮助我们捕获异常,但也不能滥用。在一些情况下,我们不应该忽略异常或捕获所有异常,而是应该尽量通过正确的设计来避免异常的发生。 - 异常的处理策略:在一些情况下,
catch块只输出异常信息而不处理异常,这对于调试非常有用,但在生产环境中,应当根据实际需求进行处理或重抛异常。
4.6 不建议在生产代码中“骗毕业设计”
根据上面的例子,我们可以看到 try-catch 能有效捕获异常并继续执行程序。然而,在实际工作中,我们不应该随意捕获异常而不做任何处理,这可能会掩盖程序中的潜在问题。
例如,以下代码:
try {
// 一些可能抛出异常的代码
} catch (Exception e) {
// 忽略异常,不做任何处理
// 这种做法是不推荐的
}
这种做法虽然在短期内不会导致程序崩溃,但会让程序隐瞒错误,给未来的调试和维护带来困难。因此,应该尽量确保异常有明确的处理方式或在必要时进行日志记录。
4.7 总结
try-catch是 Java 中处理异常的主要方式,通过它可以捕获并处理运行时异常。finally块用于确保一定会执行的代码,通常用于资源释放等清理操作。- 不要滥用
try-catch,尽量避免捕获所有异常而不做处理,要根据业务需求合理捕获并处理异常。
5. NullPointerException 空指针异常
NullPointerException(空指针异常)是 Java 中常见的一种运行时异常。它通常在程序试图访问一个尚未初始化(即值为 null)的对象或调用一个 null 引用的实例方法时发生。
5.1 常见引发空指针异常的场景
空指针异常通常发生在以下几种情况下:
-
调用空对象的实例方法:在
null对象上调用方法时,会抛出空指针异常。String str = null; int length = str.length(); // 这里会抛出 NullPointerException -
访问或修改
null对象的字段:如果你尝试访问或修改一个值为null的对象字段,也会发生空指针异常。MyClass obj = null; int value = obj.someField; // 会抛出 NullPointerException -
将
null当作数组进行操作:如果尝试对一个null引用当作数组来进行操作,比如访问数组的元素或获取数组长度,也会抛出空指针异常。int[] arr = null; int len = arr.length; // 这里会抛出 NullPointerException -
将
null作为Throwable值抛出:如果尝试将null抛出作为异常,也会导致空指针异常。throw null; // 会抛出 NullPointerException
5.2 代码示例
在下面的代码示例中,我们故意引发空指针异常,通过访问一个值为 null 的数组元素来展示这一行为。
package com.nix.demo;
import org.junit.Test;
public class DemoTest {
@Test
public void demo() {
int[] a = null; // 将数组设置为null
// 尝试访问空数组的元素,导致 NullPointerException
System.out.println(a[1]); // 会抛出 NullPointerException
}
}
5.3 运行结果
当程序执行时,它会尝试访问数组 a 的元素,但由于 a 为 null,访问 a[1] 时会抛出 NullPointerException,输出如下:
Exception in thread "main" java.lang.NullPointerException: Cannot load from int array because "a" is null
at com.nix.demo.DemoTest.demo(DemoTest.java:10)
5.4 解决空指针异常的方法
为了避免空指针异常,可以采取以下几种方式:
-
检查是否为
null:在访问对象或数组之前,可以通过if语句检查对象是否为null。if (a != null) { System.out.println(a[1]); } else { System.out.println("数组a为null"); } -
使用
Optional:Java 8 引入了Optional类,它可以帮助你避免直接操作null值。通过Optional可以更安全地处理可能为null的值。Optional<String> str = Optional.ofNullable(null); System.out.println(str.orElse("默认值")); -
使用注解:一些工具(如
@NotNull,@Nullable)可以帮助你标明哪些变量不应该为null,这有助于提高代码的可读性和安全性。 -
初始化对象:在声明对象时尽量初始化,避免后续使用时为
null。String str = "";
5.5 小结
NullPointerException是由于对null引用执行不允许的操作(如调用方法、访问字段、数组操作等)时抛出的异常。- 为了避免空指针异常,程序员应确保在使用对象前进行
null检查,并采取适当的异常处理措施。 - 在实际编程中,
null应该尽量避免使用,特别是当能通过其他方式避免null的出现时。
6. throws 的使用
在 Java 中,throws 是用于声明方法可能抛出的异常的关键字。它通常与 throw 关键字一起使用,throw 用于实际抛出异常,而 throws 用于在方法声明中指定该方法可能抛出的异常类型。使用 throws 可以让调用者知道该方法可能会发生异常,从而采取适当的处理措施。
6.1 throw 和 throws 的区别
-
throw:用于在方法体内抛出异常,表示程序运行过程中某个条件触发时需要手动抛出异常。throw new FileNotFoundException("文件未找到!"); -
throws:用于在方法声明中指定该方法可能会抛出的异常类型,告知调用该方法的代码,可能会遇到该异常。throws通常用于方法签名中。public void demo1() throws FileNotFoundException { // 可能会抛出异常 }
6.2 throws 的作用
throws 关键字并不处理异常,它仅仅是声明一个方法可能抛出某种类型的异常。这样做有助于:
- 让调用该方法的代码提前知晓可能会遇到的异常,从而可以适当地进行异常处理。
- 使得代码在出现异常时,能够清晰地知道是哪一段代码引起了问题。
6.3 示例代码
在以下代码中,我们演示了 throws 的使用,抛出了 FileNotFoundException 异常,表示文件未找到时,应该抛出异常:
package com.nix.demo;
import org.junit.Test;
import java.io.FileNotFoundException;
public class DemoTest {
@Test
public void demo1() throws FileNotFoundException {
// 模拟文件是否存在的判断
boolean fileExists = false; // 假设文件不存在
if (!fileExists) {
throw new FileNotFoundException("文件不存在!");
}
}
}
6.4 运行结果
当程序执行时,如果 fileExists 为 false,将会抛出 FileNotFoundException,输出类似以下内容:
Exception in thread "main" java.io.FileNotFoundException: 文件不存在!
at com.nix.demo.DemoTest.demo1(DemoTest.java:10)
at com.nix.demo.DemoTest.demo1(DemoTest.java:9)
6.5 异常声明
如果方法可能抛出某种异常,则需要在方法签名中使用 throws 来声明该异常。例如,如果某个方法可能会抛出 IOException 或 FileNotFoundException,你需要在方法签名中使用 throws 声明:
public void readFile(String fileName) throws FileNotFoundException, IOException {
// 代码逻辑
}
6.6 小结
throw用于抛出异常,是在方法体内进行操作。throws用于声明一个方法可能会抛出异常,是在方法声明中进行声明。- 使用
throws可以让方法的调用者知道可能会抛出哪些异常,从而进行适当的异常处理。
在实际开发中,throws 和 throw 是异常处理机制的重要组成部分,合理使用可以提高代码的可维护性和健壮性。
7. 通过调用函数的形式抛出异常
在 Java 中,我们不仅可以通过手动 throw 异常,还可以通过调用方法来抛出异常。通过在方法签名中声明 throws 来指明该方法可能会抛出某种异常,然后在方法体内根据条件进行异常的抛出。这样可以确保调用方法时,调用者能够清晰地看到可能出现的异常,并进行适当的异常处理。
7.1 自定义异常
在示例中,我们定义了一个名为 MyException 的自定义异常类,继承自 Exception,并在方法 sum() 中检查输入条件,如果不符合要求则抛出 MyException 异常。
package com.nix.demo;
public class MyException extends Exception {
public MyException(String message) {
super(message); // 调用父类构造函数,传递错误消息
}
}
自定义异常类继承 Exception,这样它就成为一个受检异常(Checked Exception),需要通过 throws 声明在方法签名中。
7.2 示例代码解析
在这个例子中,sum() 方法用于计算两个数字的和,但只有在两个数字都在 0 到 10 之间时才能执行。如果任一数字不符合条件,就会抛出 MyException 异常。
package com.nix.demo;
import org.junit.Test;
public class DemoTest2 {
// 进行加法计算,并在不符合要求时抛出异常
public int sum(int a, int b) throws MyException {
// 如果参数不在规定范围内,则抛出自定义异常
if (a > 10 || b > 10 || a < 0 || b < 0) {
throw new MyException("只能求10以内的加法");
}
return a + b;
}
@Test
public void demo() {
try {
// 调用sum方法,传入不符合条件的参数
int number = sum(100, 200);
} catch (MyException e) {
// 捕获并处理异常
e.printStackTrace();
}
}
}
7.3 代码执行
在 sum() 方法中,我们检查输入的数字。如果任一数字超出范围(即大于 10 或小于 0),我们通过 throw new MyException("错误信息") 抛出一个自定义的异常。
// 当参数为 (100, 200) 时,会抛出 MyException 异常
throw new MyException("只能求10以内的加法");
demo() 方法通过 try-catch 语句捕获并打印异常信息。异常被捕获后,e.printStackTrace() 会输出异常堆栈跟踪信息,帮助开发者了解异常发生的位置和原因。
7.4 运行结果
当我们执行 demo() 方法时,传入的数字 100 和 200 超出了合法范围,导致抛出 MyException 异常。输出的异常信息如下:
com.nix.demo.MyException: 只能求10以内的加法
at com.nix.demo.DemoTest2.sum(DemoTest2.java:6)
at com.nix.demo.DemoTest2.demo(DemoTest2.java:14)
7.5 异常的好处
- 自定义异常:通过自定义异常
MyException,可以明确区分不同类型的错误,有助于代码的可读性和维护性。 - 方法签名的声明:
sum()方法通过throws声明可能会抛出异常,调用者在调用该方法时必须处理异常(使用try-catch或throws)。 - 异常处理:
try-catch语句捕获并处理异常,避免程序崩溃或错误的扩展到其他地方。
7.6 小结
通过调用函数的方式抛出异常,结合自定义异常和 throws 机制,可以有效地将异常处理逻辑从主业务逻辑中分离,增强代码的可维护性。在实际开发中,遇到不合法的输入或执行状态时,可以通过抛出异常来通知调用者进行必要的处理。
8. 手把手教你编写自定义异常
在这个例子中,我们展示了如何创建一个自定义异常 NixException,并通过枚举类 NixCodeEnum 来管理不同的错误码和错误信息。
我们将异常类、枚举类和接口整合到一起,形成了一个结构清晰、可维护的异常处理方案。这个方案的好处是可以通过错误码统一管理不同类型的异常,从而让异常处理更加规范化和一致化。
8.1 自定义错误码接口 ErrorCode
首先,定义了一个 ErrorCode 接口,该接口规定了获取错误码和错误信息的方法。每个错误码类都需要实现这个接口,以确保每个错误都能够获取到统一的错误码和错误信息。
package com.nix.demo;
public interface ErrorCode {
/**
* 获取错误码
* @return 错误码
*/
String getCode();
/**
* 获取错误信息
* @return 错误信息
*/
String getMessage();
}
8.2 创建枚举类 NixCodeEnum
接下来,我们创建了一个枚举类 NixCodeEnum,用于存储具体的错误码和错误信息。每一个枚举值代表一种错误类型,它包括了错误码(code)和错误信息(msg)。通过 getCode() 和 getMessage() 方法,我们可以在代码中获取相应的错误码和错误信息。
package com.nix.demo;
public enum NixCodeEnum implements ErrorCode {
NOT_FOUND_PAGE("404", "找不到网站资源"),
NOT_FOUND_FILE("888", "找不到文件"),
NOT_O_TEN("bad", "只能求10以内的加法");
private final String code;
private final String msg;
NixCodeEnum(String code, String msg) {
this.code = code;
this.msg = msg;
}
@Override
public String getCode() {
return code;
}
@Override
public String getMessage() {
return msg;
}
}
通过枚举类,我们不仅可以定义多个错误码,还可以为每个错误码配备具体的错误信息。这样,当出现异常时,我们可以通过枚举类提供的错误码和错误信息来描述问题。
8.3 自定义异常类 NixException
接下来,定义自定义异常类 NixException,继承自 RuntimeException。构造方法接收 ErrorCode 类型的参数,将错误信息传递给 RuntimeException 的构造函数。
package com.nix.demo;
public class NixException extends RuntimeException {
public NixException(ErrorCode errorCode) {
super(errorCode.getMessage());
}
}
这里的 NixException 继承了 RuntimeException,意味着它是一个运行时异常。异常信息来自于传入的 ErrorCode 对象,它会通过构造函数的 super() 方法传递给 RuntimeException。
8.4 使用自定义异常
最后,在 sum 方法中使用自定义异常。当传入的参数不满足要求时,抛出 NixException 异常,并通过枚举 NixCodeEnum 中的 NOT_O_TEN 错误码来描述这个错误。
package com.nix.demo;
import org.junit.Test;
public class DemoTest {
public int sum(int a, int b) throws NixException {
// 如果参数超出范围,则抛出自定义异常
if (a > 10 || b > 10 || a < 0 || b < 0) {
throw new NixException(NixCodeEnum.NOT_O_TEN);
}
return a + b;
}
@Test
public void demo() {
try {
int number = sum(100, 200);
} catch (NixException e) {
// 捕获并打印异常信息
e.printStackTrace();
}
}
}
8.5 代码执行
在 sum() 方法中,我们检查输入的数字是否超出范围,如果超出则抛出 NixException 异常。异常通过 NixCodeEnum.NOT_O_TEN 传递错误码和错误信息。
运行 demo() 方法时,传入的数字 100 和 200 不满足要求,因此抛出 NixException 异常。异常信息通过 e.printStackTrace() 打印出来,输出如下:
com.nix.demo.NixException: 只能求10以内的加法
at com.nix.demo.DemoTest.sum(DemoTest.java:6)
at com.nix.demo.DemoTest.demo(DemoTest.java:14)
8.6 总结
通过自定义异常和错误码,我们能够灵活地管理不同类型的错误,并提供详细的错误信息。通过枚举类来组织错误码,异常类可以更加规范、易于维护。这种方法非常适用于需要统一处理多种类型错误的应用场景。
优势
- 清晰的错误处理:通过枚举类
NixCodeEnum管理错误码,使得错误的定义和管理更清晰。 - 可扩展性:可以轻松添加更多的错误码和错误信息,而不需要修改异常类。
- 统一的异常处理:自定义异常
NixException使得异常捕获和处理更加一致和易于维护。

被折叠的 条评论
为什么被折叠?



