一、try-catch-finally
机制
1. 基本结构
java
复制
try {// 可能抛出异常的代码
}
catch(异常类型1 e) {// 处理特定异常
}
catch(异常类型2 e) {// 处理其他异常
}
finally {// 无论是否发生异常,都会执行的代码(常用于资源释放)
}
2. 核心规则
-
try
块:包裹可能抛出异常的代码。 -
catch
块:捕获并处理特定类型的异常(按异常类型从具体到一般排序)。 -
finally
块:始终执行,即使有 return
或 continue
。 - 资源释放:推荐使用
try-with-resources
(Java 7+)自动关闭资源(如文件流、数据库连接)。
3. 示例
(1) 基础用法
java
复制
try {int result = 10 / 0; // 抛出 ArithmeticException
}
catch (ArithmeticException e) {System.out.println("除数不能为零: " + e.getMessage());
}
finally {System.out.println("finally 块总是执行");
}
(2) finally
与 return
java
复制
public int testFinally() {try {return 1;} catch (Exception e) {return 2;} finally {System.out.println("执行 finally");// return 3; // ⚠️ 如果 finally 中有 return,会覆盖 try/catch 的返回值!}
}
// 输出:执行 finally → 返回 1
(3) try-with-resources
(自动关闭资源)
java
复制
try (FileInputStream fis = new FileInputStream("test.txt")) {// 文件操作
} catch (IOException e) {e.printStackTrace();
}
// 文件流会自动关闭,无需手动调用 fis.close()
二、自定义异常
1. 为什么需要自定义异常?
- 业务语义化:表达特定业务错误(如用户年龄非法)。
- 异常分类:区分不同场景的异常,提高代码可读性。
2. 实现步骤
- 继承
Exception
或 RuntimeException
: - 检查型异常(Checked):继承
Exception
,需在方法签名中声明或捕获。 - 非检查型异常(Unchecked):继承
RuntimeException
,无需强制处理。
- 添加构造方法:通常提供无参构造和带消息的构造。
3. 示例
(1) 定义自定义异常
java
复制
// 自定义检查型异常
public class InvalidAgeException extends Exception {public InvalidAgeException() {super();}public InvalidAgeException(String message) {super(message);}
}// 自定义非检查型异常
public class BusinessException extends RuntimeException {public BusinessException(String message) {super(message);}
}
(2) 抛出并捕获自定义异常
java
复制
public class UserService {public void registerUser(int age) throws InvalidAgeException {if (age < 0) {throw new InvalidAgeException("年龄不能为负数");}// 其他注册逻辑}
}// 调用方处理异常
public class Main {public static void main(String[] args) {UserService service = new UserService();try {service.registerUser(-5);} catch (InvalidAgeException e) {System.out.println("注册失败: " + e.getMessage());}}
}
三、throws
与 throw
1. 核心区别
关键字 | 作用 | 使用场景 |
---|
throw | 手动抛出一个异常对象 | 在方法内部检测到错误时主动抛出异常 |
throws | 声明方法可能抛出的异常类型 | 在方法签名中声明异常,由调用者处理 |
2. 示例
(1) throw
抛出异常
java
复制
public class BankAccount {private double balance;public void withdraw(double amount) {if (amount > balance) {throw new IllegalArgumentException("余额不足"); // 抛出运行时异常}balance -= amount;}
}
(2) throws
声明异常
java
复制
public class FileProcessor {public void readFile(String path) throws IOException {FileInputStream fis = new FileInputStream(path);// 文件操作可能抛出 IOException}
}// 调用方必须处理异常
public class Main {public static void main(String[] args) {FileProcessor processor = new FileProcessor();try {processor.readFile("test.txt");} catch (IOException e) {e.printStackTrace();}}
}
四、异常分类与处理策略
1. 异常分类
类型 | 说明 | 示例 |
---|
Checked Exception | 必须显式处理(捕获或声明抛出) | IOException , SQLException |
Unchecked Exception | 无需强制处理(通常是程序逻辑错误) | NullPointerException , ArithmeticException |
2. 最佳实践
- 避免过度捕获:只在必要处捕获异常,让上层处理更合适。
- 不要忽略异常:捕获后至少记录日志或进行清理操作。
- 使用具体异常类型:避免直接捕获
Exception
,细化到具体异常。 - 自定义异常用于业务逻辑:如
InvalidInputException
、PaymentFailedException
。
五、综合示例:订单支付场景
java
复制
public class PaymentService {// 自定义异常public static class PaymentFailedException extends Exception {public PaymentFailedException(String message) {super(message);}}// 模拟支付方法public void pay(double amount) throws PaymentFailedException {if (amount <= 0) {throw new IllegalArgumentException("金额必须大于零");}boolean success = false; // 模拟支付失败if (!success) {throw new PaymentFailedException("支付失败,请重试");}}
}public class Main {public static void main(String[] args) {PaymentService service = new PaymentService();try {service.pay(-100); // 触发 IllegalArgumentException} catch (IllegalArgumentException e) {System.out.println("参数错误: " + e.getMessage());} catch (PaymentService.PaymentFailedException e) {System.out.println("支付异常: " + e.getMessage());} finally {System.out.println("支付流程结束");}}
}
六、总结
-
try-catch-finally
:确保资源释放和异常处理,finally
始终执行。 - 自定义异常:增强代码可读性,明确业务错误类型。
-
throws
vs throw
: throw
:在方法内主动抛出异常。throws
:在方法签名中声明异常,传递处理责任。
- 异常处理原则:
- 不捕获无法处理的异常。
- 不忽略异常(至少记录日志)。
- 使用具体异常类型,避免过度泛化。