异常是什么
异常是指程序执行过程中可能出现的错误或意外情况。
异常有哪些种类
编译异常
Checked Exception:这类异常在代码编译期间就被检测出来,必须显式地进行处理,否则编译不通过。常见的Checked Exception包括IOException、SQLException等。
编译时异常是指在编译阶段可能会出现的异常,这些异常需要在代码中进行明确的处理,否则无法通过编译。例如,FileNotFoundException(文件未找到)、IOException(输入输出异常)等都是编译时异常的例子。编译时异常通常是由于外部资源操作或者错误的输入导致的,因此编译器要求程序员在代码中显式地进行异常处理,以保证程序的健壮性。
运行异常
Unchecked Exception:也称为RuntimeException及其子类异常,这类异常通常是由程序员编码不规范或逻辑错误导致的,编译器在编译期间不做强制性检查,因此也不需要强制进行异常处理。常见的Unchecked Exception包括NullPointerException、IndexOutOfBoundsException等。
运行时异常是指在程序运行过程中可能会出现的异常,这些异常通常是由程序逻辑错误导致的,如空指针引用、数组下标越界等。与编译时异常不同,对于运行时异常,编译器不要求程序员进行强制的异常处理
Throwable(可抛出):是所有异常的根类,它有两个直接子类:Error和Exception。
Error:表示严重的问题,一般是由虚拟机报告的,程序无法处理。例如,OutOfMemoryError、StackOverflowError等。
Exception:表示程序运行中可能出现的异常情况,分为两种类型:受检异常(Checked Exception)和非受检异常(Unchecked Exception)。
RuntimeException:是Unchecked Exception的父类,继承自Exception类
- Checked
Exception:必须显式处理的异常,继承自Exception类但不是RuntimeException类的异常。例如,IOException、SQLException等。 - Unchecked
Exception:编译器不要求显式处理的异常,继承自RuntimeException类的异常。例如,NullPointerException、IndexOutOfBoundsException等。 - ClassNoteFoundExcepton异常:
- FileNotFoundException异常:
异常如何处理
编译异常处理注意事项
异常处理的方式:编译时异常通常需要使用try-catch块来捕获和处理。在可能抛出异常的代码块周围使用try块,并在catch块中处理异常情况。
抛出异常: 如果当前方法不适合处理异常,也可以选择在方法签名中声明抛出异常,使用throws关键字将异常传播给调用方。
资源关闭: 在处理文件IO、网络连接等资源时,需要确保资源在使用完毕后被正确关闭。通常使用try-with-resources语句(Java 7及以上版本)来自动关闭资源,以避免资源泄漏。
异常信息记录:在捕获异常时,应该记录异常信息,包括异常类型、发生的位置、以及可能的原因。这有助于排查和修复问题。
适当的异常处理策略:根据具体的业务需求和程序逻辑,为编译时异常选择适当的处理策略,可能是重试操作、回滚数据、提供默认值等。
易读性和可维护性:良好的异常处理代码应当易于阅读和理解,同时保持代码的可维护性。避免过度捕获异常或者不恰当地隐藏异常信息。
运行异常处理注意事项
异常捕获与处理:虽然对于运行时异常并不要求强制进行异常处理,但良好的编程实践通常会包括对运行时异常的捕获和处理。通过try-catch块来捕获运行时异常,可以避免程序意外终止。
异常日志记录:在捕获运行时异常时,建议记录异常信息到日志文件中,包括异常堆栈轨迹、发生的时间等,以便后续排查问题。
异常处理策略:根据具体业务需求和程序逻辑,为运行时异常选择适当的处理策略。可能是向用户显示友好的错误信息、进行数据回滚操作或者执行紧急处理逻辑等。
避免过度捕获:避免过度捕获运行时异常,尤其是使用catch(Exception e)这样的泛化异常捕获方式。应该尽量精确捕获需要处理的异常类型,以避免隐藏潜在问题。
异常处理链:在捕获和处理运行时异常时,可以构建异常处理链,将异常传递给调用方处理或者上层调用栈进行处理,以便更灵活地处理异常情况。
协作与通知:在捕获运行时异常后,需要考虑如何通知相关的模块或功能,以便进行协作和进一步处理。
异常的应用
错误处理:异常是用来处理程序执行过程中可能出现的错误情况的一种机制。通过抛出和捕获异常,可以有效地处理各种异常情况,如空指针异常、数组越界异常等,从而提高程序的健壮性和可靠性。
异常传递:在方法调用链中,如果某个方法无法处理异常情况,可以选择将异常传递给调用方处理,直到找到合适的异常处理位置。这种异常传递机制帮助实现模块之间的协作与通信。
事务管理:在数据库操作或其他涉及事务处理的场景中,异常常常被用来进行事务回滚操作。当发生异常时,可以捕获异常并执行事务回滚,以确保数据的一致性和完整性。
资源管理:在需要释放资源(如文件、网络连接、数据库连接等)的情况下,异常处理可以确保资源得到正确释放,避免资源泄露问题。
自定义异常:除了Java提供的标准异常类外,程序员还可以根据实际需求定义自己的异常类,用于特定场景下的异常处理。通过自定义异常可以更准确地描述异常情况,并提供更详细的异常信息。
自定义异常
// 自定义异常类
class MyCustomException extends Exception {
public MyCustomException(String message) {
super(message);
}
}
// 使用自定义异常的示例
class CustomExceptionExample {
public static void main(String[] args) {
try {
int age = -1; // 假设这里是从用户输入或其他来源获取的年龄
if (age < 0) {
throw new MyCustomException("年龄不能为负数");
}
System.out.println("年龄为:" + age);
} catch (MyCustomException e) {
System.err.println("发生自定义异常:" + e.getMessage());
// 这里可以进行进一步的异常处理,比如记录日志、通知相关模块等
}
}
}
首先定义了一个自定义异常类MyCustomException,它继承自Exception类。在自定义异常类中,我们可以编写自己的构造函数和其他方法,以满足特定的异常处理需求。
然后,在CustomExceptionExample类的main方法中,我们模拟了一个场景:当从外部获取到年龄值为负数时,抛出自定义异常MyCustomException。在catch块中捕获并处理了这个自定义异常,打印了异常信息,并进行了简单的异常处理操作。
throw 和 throws的区别是什么
throws:
throws关键字用于在方法声明中指定可能会抛出的异常类型,表示该方法可能会抛出某些异常,并由调用方处理这些异常。
当一个方法中可能会引发异常,但不想在方法内部处理异常时,可以使用throws关键字将异常向上一层调用栈传递,直到有地方捕获并处理这些异常。
一个方法可以声明多个异常类型,用逗号分隔,如void myMethod() throws IOException, SQLException { … }。
throw:
throw关键字用于在程序中手动抛出一个异常对象。即在代码中人为地创建一个异常对象,并将其抛出,从而触发异常处理机制。
通常在代码执行过程中遇到特定情况需要抛出异常时使用throw关键字,比如参数校验失败、业务逻辑错误等。
throw后面通常跟着一个异常对象,如throw new IOException(“File not found”);。
总结:
throws用于方法声明中,表示可能会抛出的异常类型,由调用方处理。
throw用于实际代码中,手动抛出异常对象。
// 使用throws关键字声明可能会抛出异常
void myMethod() throws IOException {
// 方法内部可能会抛出IOException
}
// 使用throw关键字手动抛出异常
void checkAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("年龄不能为负数");
}
}