什么是异常?
异常(Exception)是在程序执行期间发生的问题或错误的信号。它表示程序运行过程中出现了一些不正常的情况,可能导致程序无法正常继续执行。
异常可以分为两类:Checked Exception(编译时异常)和 Unchecked Exception(运行时异常)。
- Checked Exception(编译时异常): 这类异常在代码编译阶段就会被检测到,程序员必须在代码中进行显式的处理或声明。通常是一些不可控的外部因素导致的异常,比如文件不存在、网络连接中断、数据库访问失败等。在Java中,所有继承自
Exception
类,但不继承自RuntimeException
的异常都属于 Checked Exception。 - Unchecked Exception(运行时异常): 这类异常通常是由程序执行时的错误或逻辑错误导致的,是一种在运行时才能被检测到的异常。与 Checked Exception 不同,程序员在代码中不是强制性地要求处理或声明这类异常。在Java中,所有继承自
RuntimeException
的异常都属于 Unchecked Exception。
Throwable 类概述
Throwable
类是 Java 异常体系的根类,它是所有异常(Exception)和错误(Error)的基类。
继承关系:
php
Throwable
--| Exception // 异常,可被处理,程序还有拯救的可能性
--| Error // 错误,通常表示系统级别的问题,GG思密达
常用构造方法:
Throwable();
:无参数构造方法,创建一个Throwable
对象,其中存储的异常或错误信息为 null。Throwable(String message);
:带有消息参数的构造方法,创建一个Throwable
对象,其中存储的异常或错误信息为指定的 message。
常用方法:
String getMessage();
:获取Throwable
对象中存储的异常或错误信息。String toString();
:返回当前异常或错误的简要描述。void printStackTrace();
:展示错误的前因后果,以红色字体显示在控制台上。
Error 和 Exception 的区别
Exception
表示可处理的异常情况,程序员有能力通过捕获和处理它来进行优雅的错误处理;
Error
表示不可处理的错误,通常由系统级问题引起,只能尽力避免。
如何处理异常?
1、捕获异常: 使用 try-catch
语句块捕获可能抛出异常的代码段,以便在异常发生时执行相应的处理逻辑。
csharp
try {
// 可能抛出异常的代码
} catch (ExceptionType e) {
// 处理异常的代码
}
2、多重捕获: 可以使用多个 catch
块来捕获不同类型的异常,以执行不同的处理逻辑。
csharp
try {
// 可能抛出异常的代码
} catch (ExceptionType1 e1) {
// 处理第一种异常的代码
} catch (ExceptionType2 e2) {
// 处理第二种异常的代码
}
3、finally 块: finally
块中的代码总是会在 try
块执行后不论是否发生异常都会被执行,用于释放资源或执行必须完成的操作。
php
try {
// 可能抛出异常的代码
} catch (Exception e) {
// 处理异常的代码
} finally {
// 总是执行的代码块,用于清理资源等
}
4、抛出异常: 使用 throw
关键字手动抛出异常,将异常传播给上层调用者。
arduino
if (condition) {
throw new CustomException("Error message");
}
5、自定义异常: 可以创建自定义异常类,继承自 Exception
或其子类,以满足特定应用场景的异常需求。
scala
public class CustomException extends Exception {
// 构造方法等...
}
6、try-with-resources: 在 Java 7 及以上版本,可以使用 try-with-resources
语句来自动关闭实现 AutoCloseable
接口的资源,如文件、网络连接等。
java
try (ResourceType resource = new ResourceType()) {
// 使用资源的代码
} catch (Exception e) {
// 处理异常的代码
}
7、throws: 是 Java 中用于在方法签名中声明可能抛出的受检查异常的关键字。当一个方法可能抛出某些异常,但不进行处理时,可以使用 throws
关键字在方法声明中指定这些异常。
java
ReturnType methodName(ParameterList) throws ExceptionType1, ExceptionType2, ... {
// 方法体
}
异常是如何产生的?(从堆栈层面看)
- 方法调用: 当一个方法被调用时,Java 虚拟机(JVM)会在调用栈中为该方法创建一个新的帧(frame)。每个帧包含了该方法的局部变量、操作数栈和方法返回地址等信息。
- 异常抛出: 在方法执行过程中,如果发生了异常情况(比如除零错误、空指针引用等),该异常将被封装成一个异常对象,并在当前方法中抛出。
- 异常传播: 抛出的异常会沿着调用栈向上传播,寻找能够处理这个异常的代码块。如果在当前方法中没有合适的异常处理机制,异常会沿着调用栈向上一层一层传递,直到找到合适的异常处理代码。
- 异常处理: 当异常到达调用栈的某一层,并且找到了能够处理该异常的代码块(比如使用
try-catch
块),异常将在这一层被捕获,并且执行相应的处理代码。 - 调用栈展开: 异常处理完成后,程序将从异常发生的地方继续执行,调用栈将会逐层展开,回到异常发生的地方之后的代码继续执行。
为什么不使用"大"异常(Exception)
你是否一直以来都有一个疑问,既然Exception能包括一切异常为什么不直接使用Exception呢?事实证明,在日常开发中我经常遇事不决就Exception,但是这种方式是错误的(反面教材哈)。
- 失去精确性:
Exception
是所有异常类的父类,声明throws Exception
无法准确指定方法可能抛出的具体异常类型,丧失了异常信息的精确性。 - 不利于调试: 使用
throws Exception
可能会隐藏真正的问题,不清晰地指明方法可能发生的异常类型,使得调试过程变得困难。 - 不符合规范: Java 编码规范推荐明确指定方法可能抛出的异常类型,以提高代码的清晰性和可读性。
- 不方便处理: 调用者在处理异常时,更希望知道具体的异常类型,以便根据不同的异常情况采取不同的处理策略。使用
throws Exception
使得异常处理变得笼统和不灵活。
日常开发中常见的异常有哪些?
编译时异常(Checked Exceptions):
这些异常在编写代码时必须进行处理,要么使用 try-catch
块捕获,要么在方法签名中使用 throws
关键字声明。如果不处理,编译器会报错。
IOException
- 输入输出异常
ini
try {
FileReader fileReader = new FileReader("nonexistentfile.txt");
} catch (IOException e) {
e.printStackTrace();
}
SQLException
- SQL异常
ini
try {
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db", "user", "password");
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT * FROM nonexistingtable");
} catch (SQLException e) {
e.printStackTrace();
}
ClassNotFoundException
- 类未找到异常
ini
try {
Class<?> clazz = Class.forName("com.example.NonExistingClass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
运行时异常(Unchecked/Runtime Exceptions):
这些异常在编写代码时不强制要求进行处理,可以不使用 try-catch
或者 throws
进行异常处理。这些异常通常由于编程错误引起,而不是外部因素。
NullPointerException
- 空指针异常
ini
String str = null;
int length = str.length(); // 这里会抛出 NullPointerException
ArrayIndexOutOfBoundsException
- 数组越界异常
ini
int[] arr = {1, 2, 3};
int value = arr[5]; // 这里会抛出 ArrayIndexOutOfBoundsException
IndexOutOfBoundsException
- 索引越界异常
ini
List<String> list = new ArrayList<>();
String value = list.get(10); // 这里会抛出 IndexOutOfBoundsException
ArithmeticException
- 算术异常
ini
int result = 10 / 0; // 这里会抛出 ArithmeticException
NumberFormatException
- 数字格式异常
ini
String str = "abc";
int value = Integer.parseInt(str); // 这里会抛出 NumberFormatException
ClassCastException
- 类型转换异常
ini
Object obj = "Hello";
Integer num = (Integer) obj; // 这里会抛出 ClassCastException
ConcurrentModificationException
- 并发修改异常
ini
List<String> list = new ArrayList<>();
Iterator<String> iterator = list.iterator();
list.add("New Element"); // 这里会抛出 ConcurrentModificationException
NoSuchElementException
- 无此元素异常
ini
List<String> list = new ArrayList<>();
Iterator<String> iterator = list.iterator();
String value = iterator.next(); // 这里会抛出 NoSuchElementException
总结
一定要多思考,如果人永远待在舒适圈的话,人永远不会成长。共勉
觉得作者写的不错的,值得你们借鉴的话,就请点一个免费的赞吧!这个对我来说真的很重要。૮(˶ᵔ ᵕ ᵔ˶)ა