18.1.4 Java异常处理机制与最佳实践
1. 异常体系结构
1.1 异常类层次结构
Throwable
├── Error (系统级错误,不应被捕获)
│ ├── OutOfMemoryError
│ ├── StackOverflowError
│ └── VirtualMachineError
└── Exception (程序级异常)
├── RuntimeException (运行时异常,非受检异常)
│ ├── NullPointerException
│ ├── IllegalArgumentException
│ ├── IndexOutOfBoundsException
│ └── ClassCastException
└── 受检异常 (编译时必须处理)
├── IOException
├── SQLException
└── ClassNotFoundException
1.2 异常分类详解
public class ExceptionClassificationDemo {
// 1. Error示例 - 不应该被捕获
public void demonstrateError() {
// StackOverflowError
recursiveMethod();
// OutOfMemoryError
List<byte[]> list = new ArrayList<>();
while (true) {
list.add(new byte[1024 * 1024]); // 持续分配内存
}
}
private void recursiveMethod() {
recursiveMethod(); // 无限递归
}
// 2. 运行时异常示例 - 非受检异常
public void demonstrateRuntimeException() {
// NullPointerException
String str = null;
int length = str.length(); // 运行时抛出NPE
// IllegalArgumentException
Thread.sleep(-1); // 非法参数
// IndexOutOfBoundsException
List<String> list = new ArrayList<>();
String item = list.get(0); // 索引越界
// ClassCastException
Object obj = "Hello";
Integer num = (Integer) obj; // 类型转换异常
}
// 3. 受检异常示例 - 编译时必须处理
public void demonstrateCheckedException() throws IOException, SQLException {
// IOException - 必须声明或捕获
FileReader file = new FileReader("nonexistent.txt");
// SQLException - 必须声明或捕获
Connection conn = DriverManager.getConnection("invalid_url");
// ClassNotFoundException - 必须声明或捕获
Class.forName("com.nonexistent.Class");
}
}
2. 异常处理语法
2.1 try-catch-finally基本语法
public class BasicExceptionHandling {
public void basicTryCatch() {
try {
// 可能抛出异常的代码
int result = 10 / 0;
} catch (ArithmeticException e) {
// 处理特定异常
System.out.println("除零异常: " + e.getMessage());
} catch (Exception e) {
// 处理其他异常
System.out.println("其他异常: " + e.getMessage());
} finally {
// 无论是否发生异常都会执行
System.out.println("清理资源");
}
}
// 多重catch的顺序很重要
public void multipleCatch() {
try {
String[] array = {"1", "2", "abc"};
for (String str : array) {
int num = Integer.parseInt(str);
System.out.println(10 / num);
}
} catch (NumberFormatException e) {
// 具体异常要放在前面
System.out.println("数字格式异常: " + e.getMessage());
} catch (ArithmeticException e) {
System.out.println("算术异常: " + e.getMessage());
} catch (Exception e) {
// 通用异常放在最后
System.out.println("其他异常: " + e.getMessage());
}
}
}
2.2 try-with-resources语法
public class TryWithResourcesDemo {
// JDK 7+ try-with-resources
public void readFileWithTryWithResources() {
try (FileReader file = new FileReader("data.txt");
BufferedReader reader = new BufferedReader(file)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.out.println("文件读取异常: " + e.getMessage());
}
// 资源会自动关闭,不需要finally块
}
// 传统方式对比
public void readFileTraditional() {
FileReader file = null;
BufferedReader reader = null;
try {
file = new FileReader("data.txt");
reader = new BufferedReader(file);
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.out.println("文件读取异常: " + e.getMessage());
} finally {
// 手动关闭资源
try {
if (reader != null) reader.close();
} catch (IOException e) {
System.out.println("关闭reader失败");
}
try {
if (file != null) file.close();
} catch (IOException e) {
System.out.println("关闭file失败");
}
}
}
// 自定义资源类
public static class CustomResource implements AutoCloseable {
private String name;
public CustomResource(String name) {
this.name = name;
System.out.println("打开资源: " + name);
}
public void doSomething() {
System.out.println("使用资源: " + name);
}
@Override
public void close() {
System.out.println("关闭资源: " + name);
}
}
public void useCustomResource() {
try (CustomResource resource = new CustomResource("数据库连接")) {
resource.doSomething();
// 可能抛出异常
throw new RuntimeException("模拟异常");
} catch (Exception e) {
System.out.println("捕获异常: " + e.getMessage());
}
// 资源会自动关闭
}
}
3. 异常抛出和声明
3.1 throw和throws的使用
public class ThrowAndThrowsDemo {
// throws声明方法可能抛出的异常
public void readFile(String filename) throws IOException, FileNotFoundException {
if (filename == null) {
// throw主动抛出异常
throw new IllegalArgumentException("文件名不能为null");
}
if (!new File(filename).exists()) {
throw new FileNotFoundException("文件不存在: " + filename);
}
// 可能抛出IOException的代码
Files.readAllLines(Paths.get(filename));
}
// 方法调用者必须处理受检异常
public void callReadFile() {
try {
readFile("data.txt");
} catch (FileNotFoundException e) {
System.out.println("文件未找到: " + e.getMessage());
} catch (IOException e) {
System.out.println("IO异常: " + e.getMessage());
}
}
// 或者继续向上抛出
public void callReadFileAndThrow() throws IOException {
readFile("data.txt"); // 继续向上抛出
}
}
3.2 异常链和异常包装
public class ExceptionChainingDemo {
public void demonstrateExceptionChaining() {
try {
lowLevelMethod();
} catch (Exception e) {
System.out.println("原始异常: " + e.getMessage());
System.out.println("异常链:");
Throwable cause = e;
while (cause != null) {
System.out.println(" " + cause.getClass().getSimpleName() + ": " + cause.getMessage());
cause = cause.getCause();
}
}
}
private void lowLevelMethod() throws Exception {
try {
// 模拟底层异常
int result = 10 / 0;
} catch (ArithmeticException e) {
// 包装异常,保留原始异常信息
throw new Exception("业务处理失败", e);
}
}
// 自定义异常包装
public class BusinessException extends Exception {
public BusinessException(String message, Throwable cause) {
super(message, cause);
}
}
public void businessMethod() throws BusinessException {
try {
// 调用可能失败的底层方法
databaseOperation();
} catch (SQLException e) {
// 包装为业务异常
throw new BusinessException("用户数据处理失败", e);
}
}
private void databaseOperation() throws SQLException {
throw new SQLException("数据库连接失败");
}
}
4. 自定义异常
4.1 自定义异常类设计
// 自定义受检异常
public class UserNotFoundException extends Exception {
private String userId;
public UserNotFoundException(String userId) {
super("用户不存在: " + userId);
this.userId = userId;
}
public UserNotFoundException(String userId, Throwable cause) {
super("用户不存在: " + userId, cause);
this.userId = userId;
}
public String getUserId() {
return userId;
}
}
// 自定义运行时异常
public class InvalidConfigurationException extends RuntimeException {
private String configKey;
private String configValue;
public InvalidConfigurationException(String configKey, String configValue) {
super(String.format("无效配置 [%s=%s]", configKey, configValue));
this.configKey = configKey;
this.configValue = configValue;
}
public InvalidConfigurationException(String configKey, String configValue, Throwable cause) {
super(String.format("无效配置 [%s=%s]", configKey, configValue), cause);
this.configKey = configKey;
this.configValue = configValue;
}
public String getConfigKey() { return configKey; }
public String getConfigValue() { return configValue; }
}
// 使用自定义异常
public class UserService {
private Map<String, User> users = new HashMap<>();
public User findUser(String userId) throws UserNotFoundException {
if (us
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
Java面试圣经 文章被收录于专栏
Java面试圣经,带你练透java圣经
