一、为什么需要异常处理?
1.1 问题的引出
public class ExceptionDemo {
public static void main(String[] args) {
System.out.println(6 / 0); // 这里会抛出ArithmeticException
System.out.println("---------------"); // 这行代码永远不会执行
}
}
运行结果:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at ExceptionDemo.main(ExceptionDemo.java:3)
1.2 异常处理的重要性
-
程序健壮性:让程序在遇到错误时不会崩溃
-
用户体验:给用户友好的错误提示,而不是一堆红色错误信息
-
代码可维护性:清晰的异常处理逻辑,便于调试和维护
-
资源管理:确保异常发生时也能正确释放资源
1.3 没有异常处理的世界
// 场景1:文件操作
public void readFile() {
File file = new File("test.txt");
// 如果没有异常处理,每次调用都要检查返回值
if (file.exists()) {
// 读取文件...
} else {
System.out.println("文件不存在");
}
}
// 场景2:网络请求
public void requestData() {
// 需要检查网络连接、服务器状态、数据格式等
// 代码会变得冗长且难以维护
}
二、Java异常体系结构
2.1 异常类层次结构
Throwable (可抛出的)
|
├── Error (错误) ← 程序无法处理的严重问题
│ ├── VirtualMachineError (虚拟机错误)
│ ├── OutOfMemoryError (内存溢出)
│ └── StackOverflowError (栈溢出)
│
└── Exception (异常) ← 程序可以处理的
│
├── 编译时异常 (Checked Exception)
│ ├── IOException
│ │ └── FileNotFoundException
│ ├── SQLException
│ ├── ClassNotFoundException
│ └── CloneNotSupportedException
│
└── 运行时异常 (Unchecked Exception / RuntimeException)
├── NullPointerException
├── ArrayIndexOutOfBoundsException
├── ArithmeticException
├── ClassCastException
├── IllegalArgumentException
│ └── NumberFormatException
└── IllegalStateException
2.2 Error vs Exception
|
特性 |
Error |
Exception |
|---|---|---|
|
严重程度 |
严重,程序无法恢复 |
可恢复,程序可以处理 |
|
处理方式 |
不应被捕获处理 |
应该被捕获处理 |
|
示例 |
OutOfMemoryError, StackOverflowError |
IOException, NullPointerException |
|
来源 |
JVM或系统底层 |
应用程序代码 |
2.3 编译时异常 vs 运行时异常
|
特性 |
编译时异常 (Checked Exception) |
运行时异常 (Unchecked Exception) |
|---|---|---|
|
检查时机 |
编译时检查 |
运行时检查 |
|
处理要求 |
必须处理(try-catch或throws) |
不强制处理 |
|
继承关系 |
Exception直接子类 |
RuntimeException子类 |
|
设计理念 |
可预期的异常 |
编程错误 |
|
示例 |
IOException, SQLException |
NullPointerException, IndexOutOfBoundsException |
2.4 常见异常详解
1. NullPointerException(空指针异常)
public class NullPointerDemo {
public static void main(String[] args) {
String str = null;
// 情况1:调用null对象的方法
// System.out.println(str.length()); // 抛出NullPointerException
// 情况2:访问null对象的属性
// System.out.println(str.toString()); // 抛出NullPointerException
// 解决方案:判空
if (str != null) {
System.out.println(str.length());
} else {
System.out.println("字符串为null");
}
// Java 8+ 的Optional方案
String result = Optional.ofNullable(str)
.map(String::length)
.map(len -> "长度:" + len)
.orElse("字符串为null");
System.out.println(result);
}
}
2. ArrayIndexOutOfBoundsException(数组下标越界)
public class ArrayIndexDemo {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
// 错误:访问不存在的索引
// System.out.println(arr[5]); // 索引范围是0-4
// 正确:检查索引边界
int index = 5;
if (index >= 0 && index < arr.length) {
System.out.println(arr[index]);
} else {
System.out.println("索引越界:" + index);
}
}
}
3. ArithmeticException(算术异常)
public class ArithmeticDemo {
public static void main(String[] args) {
int a = 10;
int b = 0;
// 错误:除数为0
// System.out.println(a / b); // 抛出ArithmeticException
// 解决方案:检查除数
if (b != 0) {
System.out.println(a / b);
} else {
System.out.println("除数不能为0");
}
// 浮点数除0的特殊情况
double x = 10.0;
double y = 0.0;
System.out.println(x / y); // Infinity
System.out.println(-x / y); // -Infinity
System.out.println(0.0 / 0.0); // NaN
}
}
4. ClassCastException(类型转换异常)
public class ClassCastDemo {
public static void main(String[] args) {
Object obj = "Hello";
// 错误:不安全的类型转换
// Integer num = (Integer) obj; // 抛出ClassCastException
// 解决方案1:使用instanceof检查
if (obj instanceof Integer) {
Integer num = (Integer) obj;
System.out.println(num);
} else {
System.out.println("obj不是Integer类型");
}
// 解决方案2:使用泛型
List<String> list = new ArrayList<>();
// 编译时检查,避免运行时异常
}
}
5. NumberFormatException(数字格式异常)
public class NumberFormatDemo {
public static void main(String[] args) {
String str1 = "123";
String str2 = "abc";
// 正确转换
int num1 = Integer.parseInt(str1);
System.out.println("转换结果:" + num1);
// 错误转换
// int num2 = Integer.parseInt(str2); // 抛出NumberFormatException
// 解决方案:异常处理
try {
int num2 = Integer.parseInt(str2);
System.out.println("转换结果:" + num2);
} catch (NumberFormatException e) {
System.out.println("数字格式错误:" + str2);
}
// 正则表达式验证
if (str2.matches("-?\\d+")) {
int num3 = Integer.parseInt(str2);
System.out.println("转换结果:" + num3);
} else {
System.out.println("不是有效的数字:" + str2);
}
}
}
6. FileNotFoundException(文件未找到异常)
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class FileNotFoundDemo {
public static void main(String[] args) {
File file = new File("不存在的文件.txt");
try (Scanner scanner = new Scanner(file)) {
while (scanner.hasNextLine()) {
System.out.println(scanner.nextLine());
}
} catch (FileNotFoundException e) {
System.out.println("文件不存在:" + file.getAbsolutePath());
// 可以创建新文件或提示用户
}
}
}
三、异常处理的三种方式
3.1 try-catch-finally 结构
基本语法
try {
// 可能抛出异常的代码
// 1. 文件操作
// 2. 网络请求
// 3. 数据库操作
// 4. 用户输入处理
} catch (异常类型1 变量名1) {
// 处理异常类型1
} catch (异常类型2 变量名2) {
// 处理异常类型2
} finally {
// 无论是否发生异常,都会执行的代码
// 通常用于资源释放
}
完整示例
import java.io.*;
public class TryCatchDemo {
public static void readFile(String filePath) {
FileReader reader = null;
BufferedReader bufferedReader = null;
try {
// 1. 打开文件
reader = new FileReader(filePath);
bufferedReader = new BufferedReader(reader);
// 2. 读取文件
String line;
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
} catch (FileNotFoundException e) {
// 文件不存在异常
System.err.println("文件未找到:" + e.getMessage());
} catch (IOException e) {
// IO异常
System.err.println("读取文件出错:" + e.getMessage());
} catch (Exception e) {
// 其他所有异常
System.err.println("发生未知错误:" + e.getMessage());
} finally {
// 3. 释放资源
try {
if (bufferedReader != null) {
bufferedReader.close();
}
if (reader != null) {
reader.close();
}
} catch (IOException e) {
System.err.println("关闭文件时出错:" + e.getMessage());
}
}
}
}
try-with-resources(Java 7+)
public class TryWithResourcesDemo {
public static void readFile(String filePath) {
// 自动关闭资源(实现了AutoCloseable接口)
try (FileReader reader = new FileReader(filePath);
BufferedReader bufferedReader = new BufferedReader(reader)) {
String line;
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
} catch (FileNotFoundException e) {
System.err.println("文件未找到:" + e.getMessage());
} catch (IOException e) {
System.err.println("读取文件出错:" + e.getMessage());
} // finally块不再需要,资源会自动关闭
}
}
3.2 throws 声明异常
方法声明异常
import java.io.*;
public class ThrowsDemo {
// 方法声明可能抛出的异常
public void processFile(String filePath)
throws FileNotFoundException, IOException {
// 不在这里处理异常,让调用者处理
try (BufferedReader reader = new BufferedReader(
new FileReader(filePath))) {
String line;
while ((line = reader.readLine()) != null) {
processLine(line); // 可能抛出其他异常
}
}
}
private void processLine(String line) throws IOException {
if (line == null || line.isEmpty()) {
throw new IOException("空行");
}
// 处理逻辑...
}
public static void main(String[] args) {
ThrowsDemo demo = new ThrowsDemo();
try {
demo.processFile("data.txt");
} catch (FileNotFoundException e) {
System.err.println("文件不存在:" + e.getMessage());
} catch (IOException e) {
System.err.println("IO错误:" + e.getMessage());
}
}
}
方法调用链中的异常传递
public class ExceptionPropagation {
public void methodA() throws IOException {
methodB(); // 向上传播异常
}
public void methodB() throws IOException {
methodC(); // 向上传播异常
}
public void methodC() throws IOException {
// 可能抛出IOException
throw new IOException("methodC异常");
}
public static void main(String[] args) {
ExceptionPropagation ep = new ExceptionPropagation();
try {
ep.methodA();
} catch (IOException e) {
// 打印完整的调用栈
e.printStackTrace();
// 获取异常信息
System.out.println("异常信息:" + e.getMessage());
System.out.println("异常类型:" + e.getClass().getName());
// 获取根本原因
Throwable cause = e.getCause();
if (cause != null) {
System.out.println("根本原因:" + cause.getMessage());
}
}
}
}
3.3 throw 抛出异常
手动抛出异常
public class ThrowDemo {
public static double divide(double a, double b) {
if (b == 0) {
// 手动抛出异常
throw new ArithmeticException("除数不能为0");
}
return a / b;
}
public static void validateAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("年龄不能为负数:" + age);
}
if (age > 150) {
throw new IllegalArgumentException("年龄不合法:" + age);
}
System.out.println("年龄验证通过:" + age);
}
public static void main(String[] args) {
// 示例1:除数为0
try {
double result = divide(10, 0);
System.out.println("结果:" + result);
} catch (ArithmeticException e) {
System.out.println("捕获到异常:" + e.getMessage());
}
// 示例2:参数验证
try {
validateAge(-5);
} catch (IllegalArgumentException e) {
System.out.println("参数错误:" + e.getMessage());
}
// 示例3:重新抛出异常
try {
validateAge(200);
} catch (IllegalArgumentException e) {
System.err.println("原始异常:" + e.getMessage());
// 包装后重新抛出
throw new RuntimeException("验证失败", e);
}
}
}
四、finally 块的深入理解
4.1 finally 的执行时机
public class FinallyDemo {
public static int test1() {
try {
System.out.println("try块执行");
return 1; // 这里会记录返回值1
} catch (Exception e) {
return 2;
} finally {
System.out.println("finally块执行");
// 注意:finally中的return会覆盖try中的return
}
}
public static int test2() {
int i = 0;
try {
i = 10;
return i; // 记录返回值10
} finally {
i = 20; // 虽然修改了i,但不影响返回值
System.out.println("finally中i=" + i);
}
}
public static int test3() {
try {
return 1;
} finally {
return 2; // finally中的return会覆盖try中的return
}
}
public static void main(String[] args) {
System.out.println("test1结果:" + test1());
// 输出:
// try块执行
// finally块执行
// test1结果:1
System.out.println("test2结果:" + test2());
// 输出:
// finally中i=20
// test2结果:10
System.out.println("test3结果:" + test3());
// 输出:test3结果:2
}
}
4.2 finally 在资源管理中的应用
public class ResourceManagement {
// 传统方式
public void processFileOld(String filePath) {
FileInputStream fis = null;
try {
fis = new FileInputStream(filePath);
// 处理文件...
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// try-with-resources方式
public void processFileNew(String filePath) {
try (FileInputStream fis = new FileInputStream(filePath);
BufferedInputStream bis = new BufferedInputStream(fis)) {
// 处理文件...
} catch (IOException e) {
e.printStackTrace();
}
// 不需要finally块,资源会自动关闭
}
// 自定义资源
static class MyResource implements AutoCloseable {
public void doSomething() {
System.out.println("MyResource正在工作...");
}
@Override
public void close() {
System.out.println("MyResource被关闭");
}
}
public static void main(String[] args) {
// 使用自定义资源
try (MyResource resource = new MyResource()) {
resource.doSomething();
} // 自动调用close()
}
}
五、异常处理的最佳实践
5.1 异常处理原则
原则1:具体异常优于通用异常
// 不推荐
try {
// 业务代码
} catch (Exception e) { // 太宽泛
e.printStackTrace();
}
// 推荐
try {
// 业务代码
} catch (FileNotFoundException e) {
// 处理文件不存在
System.err.println("文件不存在:" + e.getMessage());
} catch (IOException e) {
// 处理IO异常
System.err.println("IO错误:" + e.getMessage());
} catch (Exception e) {
// 最后捕获通用异常
System.err.println("未知错误:" + e.getMessage());
throw e; // 重新抛出
}
原则2:不要忽略异常
// 不推荐:异常被静默忽略
try {
process();
} catch (Exception e) {
// 什么也不做
}
// 推荐做法1:记录日志
try {
process();
} catch (Exception e) {
log.error("处理失败", e);
}
// 推荐做法2:包装后重新抛出
try {
process();
} catch (Exception e) {
throw new BusinessException("处理失败", e);
}
// 推荐做法3:恢复或降级
try {
process();
} catch (Exception e) {
fallbackProcess(); // 降级处理
}
原则3:不要用异常控制流程
// 不推荐:用异常控制流程
public int findIndex(String[] array, String target) {
for (int i = 0; i < array.length; i++) {
try {
if (array[i].equals(target)) {
return i;
}
} catch (NullPointerException e) {
// 跳过null
}
}
return -1;
}
// 推荐:正常控制流程
public int findIndexBetter(String[] array, String target) {
for (int i = 0; i < array.length; i++) {
if (array[i] != null && array[i].equals(target)) {
return i;
}
}
return -1;
}
原则4:在合适的层级处理异常
// 三层架构中的异常处理
public class UserService {
// 数据访问层
public class UserDao {
public User findById(int id) throws SQLException {
// 数据库操作,抛出SQLException
}
}
// 业务逻辑层
public class UserService {
private UserDao userDao = new UserDao();
public User getUserById(int id) throws BusinessException {
try {
return userDao.findById(id);
} catch (SQLException e) {
// 转换异常类型
throw new BusinessException("查询用户失败", e);
}
}
}
// 表现层
public class UserController {
private UserService userService = new UserService();
public void showUser(int id) {
try {
User user = userService.getUserById(id);
// 显示用户信息
} catch (BusinessException e) {
// 给用户友好提示
showErrorMessage("获取用户信息失败");
}
}
}
}
5.2 异常链(Chained Exceptions)
public class ExceptionChain {
public static void processData() throws DataProcessException {
try {
readDataFromFile();
} catch (IOException e) {
// 包装原始异常,形成异常链
throw new DataProcessException("数据处理失败", e);
}
}
private static void readDataFromFile() throws IOException {
throw new IOException("文件读取失败");
}
static class DataProcessException extends Exception {
public DataProcessException(String message, Throwable cause) {
super(message, cause);
}
}
public static void main(String[] args) {
try {
processData();
} catch (DataProcessException e) {
// 打印完整异常链
e.printStackTrace();
// 遍历异常链
Throwable current = e;
while (current != null) {
System.out.println("异常: " + current.getClass().getName());
System.out.println("信息: " + current.getMessage());
current = current.getCause();
}
}
}
}
六、自定义异常
6.1 为什么需要自定义异常?
-
业务语义:更准确地描述业务问题
-
异常分类:便于按类型处理不同异常
-
信息丰富:携带更多业务相关信息
-
统一处理:便于统一异常处理逻辑
6.2 创建自定义异常
// 1. 检查异常(Checked Exception)
public class BusinessException extends Exception {
// 错误码
private String errorCode;
// 错误信息
private String errorMessage;
// 构造方法
public BusinessException(String errorCode, String errorMessage) {
super(errorCode + ": " + errorMessage);
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
public BusinessException(String errorCode, String errorMessage, Throwable cause) {
super(errorCode + ": " + errorMessage, cause);
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
// Getter方法
public String getErrorCode() {
return errorCode;
}
public String getErrorMessage() {
return errorMessage;
}
}
// 2. 运行时异常(Unchecked Exception)
public class BusinessRuntimeException extends RuntimeException {
private String errorCode;
private String errorMessage;
public BusinessRuntimeException(String errorCode, String errorMessage) {
super(errorCode + ": " + errorMessage);
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
public BusinessRuntimeException(String errorCode, String errorMessage, Throwable cause) {
super(errorCode + ": " + errorMessage, cause);
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
// Getter方法
public String getErrorCode() {
return errorCode;
}
public String getErrorMessage() {
return errorMessage;
}
}
6.3 使用自定义异常
public class UserService {
// 错误码定义
public interface ErrorCode {
String USER_NOT_FOUND = "USER_001";
String USER_INVALID = "USER_002";
String USER_EXISTS = "USER_003";
}
public User findUserById(int userId) throws BusinessException {
if (userId <= 0) {
throw new BusinessException(
ErrorCode.USER_INVALID,
"用户ID不合法: " + userId
);
}
User user = userDao.findById(userId);
if (user == null) {
throw new BusinessException(
ErrorCode.USER_NOT_FOUND,
"用户不存在: " + userId
);
}
return user;
}
public void registerUser(User user) {
if (userDao.exists(user.getUsername())) {
throw new BusinessRuntimeException(
ErrorCode.USER_EXISTS,
"用户已存在: " + user.getUsername()
);
}
userDao.save(user);
}
public static void main(String[] args) {
UserService service = new UserService();
try {
// 测试检查异常
User user = service.findUserById(-1);
} catch (BusinessException e) {
System.out.println("错误码: " + e.getErrorCode());
System.out.println("错误信息: " + e.getErrorMessage());
// 根据错误码做不同处理
switch (e.getErrorCode()) {
case ErrorCode.USER_NOT_FOUND:
// 显示用户不存在提示
break;
case ErrorCode.USER_INVALID:
// 显示参数错误提示
break;
}
}
// 测试运行时异常
try {
service.registerUser(new User("admin"));
} catch (BusinessRuntimeException e) {
System.out.println("运行时异常: " + e.getMessage());
}
}
}
6.4 自定义异常的最佳实践
// 1. 定义异常基类
public abstract class BaseException extends RuntimeException {
private final ErrorCode errorCode;
private final Object data;
public BaseException(ErrorCode errorCode, String message, Object data) {
super(message);
this.errorCode = errorCode;
this.data = data;
}
public BaseException(ErrorCode errorCode, String message, Throwable cause, Object data) {
super(message, cause);
this.errorCode = errorCode;
this.data = data;
}
public ErrorCode getErrorCode() {
return errorCode;
}
public Object getData() {
return data;
}
}
// 2. 定义具体业务异常
public class UserNotFoundException extends BaseException {
public UserNotFoundException(Long userId) {
super(ErrorCode.USER_NOT_FOUND,
"用户不存在: " + userId,
Map.of("userId", userId));
}
}
public class InsufficientBalanceException extends BaseException {
public InsufficientBalanceException(BigDecimal balance, BigDecimal required) {
super(ErrorCode.INSUFFICIENT_BALANCE,
"余额不足: 当前余额" + balance + ",需要" + required,
Map.of("balance", balance, "required", required));
}
}
// 3. 统一异常处理器
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BaseException.class)
public ResponseEntity<ErrorResponse> handleBaseException(BaseException e) {
ErrorResponse response = new ErrorResponse(
e.getErrorCode().getCode(),
e.getMessage(),
e.getData()
);
return ResponseEntity.status(e.getErrorCode().getHttpStatus())
.body(response);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception e) {
ErrorResponse response = new ErrorResponse(
"SYSTEM_ERROR",
"系统内部错误",
null
);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(response);
}
}
七、异常处理的设计模式
7.1 异常转换模式
// 将底层异常转换为业务异常
public class UserRepository {
public User findById(int id) {
try {
return jdbcTemplate.queryForObject(
"SELECT * FROM users WHERE id = ?",
new UserRowMapper(),
id
);
} catch (EmptyResultDataAccessException e) {
throw new UserNotFoundException(id);
} catch (DataAccessException e) {
throw new DatabaseException("查询用户失败", e);
}
}
}
7.2 重试模式
public class RetryTemplate {
public <T> T executeWithRetry(Callable<T> task, int maxRetries) {
int retryCount = 0;
while (true) {
try {
return task.call();
} catch (Exception e) {
retryCount++;
if (retryCount >= maxRetries) {
throw new RetryExhaustedException("重试次数耗尽", e);
}
// 指数退避
long waitTime = (long) Math.pow(2, retryCount) * 1000;
try {
Thread.sleep(waitTime);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new RuntimeException("重试被中断", ie);
}
System.out.println("重试第" + retryCount + "次,等待" + waitTime + "ms");
}
}
}
}
7.3 熔断器模式
public class CircuitBreaker {
private enum State { CLOSED, OPEN, HALF_OPEN }
private State state = State.CLOSED;
private int failureCount = 0;
private final int failureThreshold = 5;
private final long timeout = 30000; // 30秒
private long lastFailureTime = 0;
public <T> T execute(Supplier<T> supplier) {
if (state == State.OPEN) {
if (System.currentTimeMillis() - lastFailureTime > timeout) {
state = State.HALF_OPEN;
} else {
throw new CircuitBreakerOpenException("熔断器已打开");
}
}
try {
T result = supplier.get();
if (state == State.HALF_OPEN) {
state = State.CLOSED;
failureCount = 0;
}
return result;
} catch (Exception e) {
failureCount++;
if (state == State.HALF_OPEN || failureCount >= failureThreshold) {
state = State.OPEN;
lastFailureTime = System.currentTimeMillis();
}
throw e;
}
}
}
八、实际应用场景总结
8.1 何时使用try-catch?
// 场景1:需要继续执行的业务逻辑
public void processOrder(Order order) {
try {
// 主要业务逻辑
validateOrder(order);
processPayment(order);
updateInventory(order);
} catch (PaymentException e) {
// 支付失败,记录日志并继续处理其他订单
log.error("订单支付失败: {}", order.getId(), e);
notifyUserPaymentFailed(order.getUserId());
} catch (InventoryException e) {
// 库存不足,记录日志
log.error("库存不足: {}", order.getProductId(), e);
notifyUserOutOfStock(order.getUserId());
} finally {
// 无论成功失败,都需要执行
updateOrderStatus(order);
}
}
8.2 何时使用throws?
// 场景1:框架或库的设计
public class FileProcessor {
/**
* 处理文件
* @param filePath 文件路径
* @throws FileNotFoundException 文件不存在
* @throws IOException 读取错误
*/
public void process(String filePath) throws FileNotFoundException, IOException {
// 让调用者决定如何处理异常
}
}
// 场景2:中间层传递异常
public class ServiceLayer {
public User getUser(int id) throws BusinessException {
try {
return dao.findById(id);
} catch (SQLException e) {
// 转换并向上传递
throw new BusinessException("查询失败", e);
}
}
}
8.3 何时使用throw?
// 场景1:参数验证
public void setAge(int age) {
if (age < 0 || age > 150) {
throw new IllegalArgumentException("年龄必须在0-150之间: " + age);
}
this.age = age;
}
// 场景2:业务规则检查
public void transfer(Account from, Account to, BigDecimal amount) {
if (from.getBalance().compareTo(amount) < 0) {
throw new InsufficientBalanceException(
from.getBalance(),
amount
);
}
// 执行转账...
}
// 场景3:快速失败
public void initialize() {
if (initialized) {
throw new IllegalStateException("已经初始化过");
}
// 初始化逻辑...
initialized = true;
}
九、常见面试题
9.1 基础问题
-
Error和Exception有什么区别?
-
运行时异常和编译时异常有什么区别?
-
finally块一定会执行吗?
-
try、catch、finally的执行顺序是怎样的?
9.2 进阶问题
-
try-with-resources是如何工作的?
-
异常链是什么?有什么作用?
-
如何自定义异常?应该继承哪个类?
-
什么时候应该抛出异常,什么时候应该捕获异常?
9.3 实践问题
-
下面的代码有什么问题?如何改进?
try { // 业务代码 } catch (Exception e) { // 空catch块 } -
finally块中的return会覆盖try块中的return吗?
-
什么是异常屏蔽(Suppressed Exceptions)?
总结
异常处理是Java编程中非常重要的部分,良好的异常处理能够:
-
提高程序的健壮性和可靠性
-
提供更好的用户体验
-
便于调试和维护
-
保证资源的正确释放
记住以下关键点:
-
区分检查异常和运行时异常
-
不要忽略异常
-
在合适的层级处理异常
-
使用具体的异常类型
-
为异常提供有用的上下文信息
-
考虑使用自定义异常表达业务含义
-
使用try-with-resources管理资源
通过合理使用异常处理机制,你可以编写出更加健壮、可维护的Java应用程序。
960



被折叠的 条评论
为什么被折叠?



