Exception(异常)
入门
/*
java.lang.ArithmeticException: / by zero
(算术异常)
*/
System.out.println(4/0);
异常的概念
异常(Exception)是在程序执行过程中发生的意外事件,它会中断程序的正常流程。在Java中,异常是一个对象,代表了程序运行时出现的错误或异常情况。
异常的主要特点包括:
- 它是程序执行期间发生的错误或异常情况
- 会导致程序中断或异常终止
- 可以被捕获和处理,从而避免程序崩溃
- 有助于分离错误处理代码和正常业务逻辑
异常处理机制允许我们在程序中优雅地处理错误情况,提高程序的健壮性和可靠性。通过适当的异常处理,我们可以:
- 检测并报告错误
- 将错误处理代码与主程序逻辑分离
- 传播错误信息
- 分组和区分不同类型的错误
在Java中,异常是以类的形式定义的,所有异常类都是Throwable类的子类。理解异常的概念和处理机制对于编写健壮的Java程序至关重要。
异常分类
Java中的异常主要分为两大类:
Checked Exceptions
(受检异常):编译时异常这些是编译器强制要求处理的异常。它们必须被 try-catch 块捕获或在方法签名中使用 throws 关键字声明。例如:IOException, SQLException。Unchecked Exceptions(
非受检异常):运行时异常这些异常在编译时不强制要求处理。它们通常是由程序逻辑错误引起的,包括 RuntimeException 及其子类。例如:NullPointerException, ArrayIndexOutOfBoundsException。
此外,还有一类特殊的异常:
- Errors:这些通常表示严重的问题,程序通常无法恢复。例如:
OutOfMemoryError, StackOverflowError
。一般不建议捕获或处理 Errors。
理解这些分类有助于我们更好地设计异常处理策略,提高程序的健壮性和可维护性。
异常体系图
Java的异常体系结构如下图所示:
在这个体系中:
- Throwable:所有错误和异常的父类
- Error:表示严重的错误,通常是不可恢复的系统级错误
- Exception:表示可以被捕获和处理的异常情况
- RuntimeException:运行时异常,是不需要强制处理的异常
- Checked Exception:编译时异常,必须被显式处理或声明抛出
理解这个体系结构有助于我们更好地处理和管理Java程序中的各种异常情况。
常见的异常
常见的Error异常及其解释:
Error类型 | 描述 |
---|---|
OutOfMemoryError | 当JVM没有足够的内存来分配给对象时抛出。这通常表示程序消耗了过多的内存资源。 |
StackOverflowError | 当方法调用栈深度超过了JVM允许的最大深度时抛出。常见于无限递归或过深的递归调用。 |
NoClassDefFoundError | 当JVM或ClassLoader实例试图加载类的定义,但找不到类定义时抛出。可能是由类路径配置错误或缺少必要的JAR文件导致。 |
VirtualMachineError | 表示JVM出现了严重问题,无法继续执行操作。这是一个抽象类,具体的错误由其子类表示。 |
InternalError | 表示JVM内部发生了意外错误。这通常指示JVM本身的bug或严重的系统级问题。 |
这些Error通常表示严重的、不可恢复的问题,大多与JVM或系统资源相关。在正常的应用程序中,我们通常不会去捕获或处理这些Error,因为它们往往超出了程序的控制范围。相反,我们应该关注于防止这些Error的发生,例如优化内存使用、避免无限递归等。
常见的Exception异常
以下是一些常见的Exception异常,分为运行时异常(RuntimeException)和编译时异常(Checked Exception):
运行时异常(RuntimeException):
异常类型 | 描述 |
---|---|
NullPointerException | 当试图使用null对象的引用时抛出 |
ArrayIndexOutOfBoundsException | 当试图访问数组中不存在的索引时抛出 |
ArithmeticException | 当出现异常的算术条件时抛出,如除以零 |
ClassCastException | 当试图将对象强制转换为不是实例的子类时抛出 |
IllegalArgumentException | 当方法接收到一个不合法或不适当的参数时抛出 |
运行时异常通常是由程序逻辑错误引起的,编译器不强制要求处理这些异常,但良好的编程实践建议处理它们。
编译时异常(Checked Exception):
异常类型 | 描述 |
---|---|
IOException | 表示发生了某种I/O异常,如文件读写错误 |
SQLException | 表示数据库访问错误或其他与数据库相关的错误 |
ClassNotFoundException | 当应用程序试图加载类时,找不到指定的类而抛出 |
InterruptedException | 当线程被另一个线程中断时抛出 |
FileNotFoundException | 当试图访问一个不存在的文件时抛出 |
编译时异常必须在代码中显式处理,要么使用try-catch块捕获,要么在方法签名中使用throws关键字声明。这些异常通常表示程序本身无法修复的外部问题。
理解这些异常的区别和特点有助于编写更健壮和可靠的Java程序。运行时异常通常可以通过改进代码逻辑来预防,而编译时异常则需要在代码中明确处理或声明。
异常处理概念
异常处理分类
在Java中,异常处理主要分为两种方式:
- 捕获异常(try-catch-finally):这是最常用的异常处理方式,用于在代码中捕获并处理可能发生的异常。
- 声明异常(throws):用于在方法签名中声明该方法可能抛出的异常,将异常处理的责任转移给调用者,最顶级的就是JVM。
- 捕获异常(try-catch-finally):
try {
// 可能抛出异常的代码
} catch (ExceptionType1 e1) {
// 处理ExceptionType1类型的异常
} catch (ExceptionType2 e2) {
// 处理ExceptionType2类型的异常
} finally {
// 无论是否发生异常,都会执行的代码
}
- 声明异常(throws):
public void someMethod() throws SomeException {
// 方法体
}
这两种方式可以根据具体情况选择使用,有时也会结合使用以实现更复杂的异常处理逻辑。
自定义异常
自定义异常是Java中一种重要的异常处理机制,允许开发者创建特定于应用程序的异常类型。这种机制有助于更精确地描述和处理程序中可能出现的特殊错误情况。
创建自定义异常的步骤:
-
- 定义一个继承自Exception(对于受检异常)或RuntimeException(对于非受检异常)的类。
-
- 通常,至少提供两个构造函数:一个无参构造函数和一个接受错误消息的构造函数。
-
- 根据需要,可以添加额外的方法或属性来提供更多关于异常的信息。
示例代码:
public class CustomException extends Exception {
public CustomException() {
super();
}
public CustomException(String message) {
super(message);
}
public CustomException(String message, Throwable cause) {
super(message, cause);
}
}
使用自定义异常的优势:
- 提高代码的可读性和可维护性
- 更精确地描述错误情况
- 允许对特定类型的错误进行特殊处理
- 有助于创建更健壮和更易于调试的应用程序
在实际应用中,应根据具体需求和场景来决定是否创建和使用自定义异常。
throw和throws
特性 | throw | throws |
---|---|---|
用途 | 用于在代码中显式抛出异常 | 用于在方法签名中声明可能抛出的异常 |
位置 | 方法内部 | 方法签名 |
后面跟随 | 异常对象 | 异常类型 |
数量 | 一次只能抛出一个异常 | 可以声明多个异常 |
强制性 | 可以抛出任何异常 | 必须声明所有未处理的受检异常 |
示例 | throw new IOException(); | void method() throws IOException, SQLException |
这个表格总结了throw和throws的主要区别。throw用于在代码中主动抛出异常,而throws用于在方法签名中声明可能抛出的异常类型。理解它们的区别对于正确处理和管理Java程序中的异常至关重要。