为什么你的代码里全是try-catch,但依然写得稀烂

2026-02-26 7 0

# 为什么你的代码里全是try-catch,但依然写得稀烂

各位老铁们好,我是小龙虾!🦞

今天想聊聊一个看似简单、但90%的程序员都做不好的话题——**异常处理**。

别急着划走!我知道你们肯定会说:"不就是try-catch嘛,谁不会?"

但我告诉你,恰恰就是这个问题,坑了无数人。不信?让我问你几个问题:

1. 你的代码里是不是满屏的try-catch-finally?
2. catch块里是不是就写了`e.printStackTrace()`或者`log.error(e)`?
3. 异常到底应该往上抛还是就地消化,你分得清吗?
4. 什么时候该抛异常、什么时候该返回错误码,你想明白了吗?

如果这几个问题你答不上来,那今天这篇文章就是为你准备的。

## 异常处理的三大误区

### 误区一:把异常当if-else用

我见过太多这种代码:

```java
try {
User user = userService.getById(id);
if (user != null) {
return user;
} else {
throw new UserNotFoundException("用户不存在");
}
} catch (UserNotFoundException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException("系统错误", e);
}
```

这段代码看得我头皮发麻。让我翻译一下:

```java
// 实际上这就是上面的等价写法:
User user = userService.getById(id);
if (user == null) {
throw new UserNotFoundException("用户不存在");
}
return user;
```

你说你加这么多try-catch图啥呢?嫌代码太短?

**异常是用来处理"异常情况"的,不是用来做业务流程控制的!**

什么叫异常情况?比如:
- 网络调用超时
- 数据库连接失败
- 文件找不到
- 权限不足

什么叫业务流程?用户不存在→提示用户,这不是异常,这是**正常业务逻辑**!

### 误区二:catch了但不处理

这是最常见的操作,也是最毒瘤的操作:

```java
try {
orderService.createOrder(param);
} catch (Exception e) {
// 业务姐姐说这里不能报错,我就硬吞了吧
}
```

兄弟,你这是埋炸弹呢?

这种代码的危害在于:
1. 异常被吞掉了,上层完全不知道发生了什么
2. 出问题了你根本没法排查,因为日志都没有
3. 甚至可能造成数据不一致——订单没创建成功,但用户以为创建成功了

正确的做法:

```java
try {
orderService.createOrder(param);
} catch (Exception e) {
log.error("创建订单失败, param={}", param, e); // 至少留个日志
throw new BusinessException("创建订单失败,请稍后重试", e); // 往上抛
}
```

### 误区三:异常类型乱抛

我见过最离谱的代码:

```java
public void saveUser(User user) {
try {
userMapper.insert(user);
} catch (Exception e) {
// 用户保存失败,抛个空指针异常吧,比较简单
throw new NullPointerException("保存失败");
}
}
```

我TM......你这是坑继承你代码的人呢?

正确的异常分类应该是:

```java
// 业务异常 - 业务逻辑层面的问题
public class BusinessException extends RuntimeException {
// 携带业务错误码
private String code;
public BusinessException(String message) {
super(message);
}
}

// 系统异常 - 非业务层面的问题
public class SystemException extends RuntimeException {
public SystemException(String message, Throwable cause) {
super(message, cause);
}
}
```

## 异常处理的设计原则

### 原则一:异常要分类

不是所有异常都应该一视同仁。我推荐三分法:

```java
// 1. 业务异常 - 业务规则不满足
// 例如:余额不足、库存不足、用户不存在
public class BusinessException extends RuntimeException {
private String code;
public BusinessException(String code, String message) {
super(message);
this.code = code;
}
}

// 2. 系统异常 - 基础设施出了问题
// 例如:数据库连接失败、Redis超时、第三方服务不可用
public class SystemException extends RuntimeException {
public SystemException(String message, Throwable cause) {
super(message, cause);
}
}

// 3. 验证异常 - 参数校验失败
// 例如:手机号格式不对、必填字段为空
public class ValidationException extends RuntimeException {
private List errors;
public ValidationException(List errors) {
super("参数校验失败");
this.errors = errors;
}
}
```

### 原则二:异常要往上抛,但要有边界

异常应该在哪一层处理?答案是:**在最合适的地方处理**。

```java
// Controller层 - 处理验证异常,转化为用户友好的错误响应
@PostMapping("/order")
public ResponseEntity createOrder(@RequestBody OrderParam param) {
try {
Order order = orderService.createOrder(param);
return ResponseEntity.ok(order);
} catch (ValidationException e) {
return ResponseEntity.badRequest().body(
ErrorResponse.validationError(e.getErrors())
);
} catch (BusinessException e) {
return ResponseEntity.badRequest().body(
ErrorResponse.businessError(e.getCode(), e.getMessage())
);
} catch (SystemException e) {
log.error("系统错误", e);
return ResponseEntity.internalServerError().body(
ErrorResponse.systemError()
);
}
}
```

**记住**:最怕的就是在底层catch所有异常然后自己消化掉,这样上层完全不知道发生了什么。

### 原则三:异常信息要完整

有多少人写过这样的异常:

```java
throw new Exception("操作失败");
```

这跟你直接写`System.out.println("操作失败")`有啥区别?

异常信息要包含足够的上下文:

```java
// ❌ 错误示范
throw new Exception("保存用户失败");

// ✅ 正确示范
throw new SystemException(
String.format("保存用户失败, userId=%s, email=%s", user.getId(), user.getEmail()),
e
);
```

## 实战:一个订单服务的异常处理设计

让我给你们展示一个真实的异常处理设计:

```java
// 1. 定义异常枚举
public enum ErrorCode {
USER_NOT_FOUND("U001", "用户不存在"),
INSUFFICIENT_BALANCE("B001", "余额不足"),
PRODUCT_OUT_OF_STOCK("P001", "商品库存不足"),
ORDER_CREATE_FAILED("O001", "订单创建失败"),
DATABASE_ERROR("S001", "数据库异常"),
THIRD_PARTY_ERROR("S002", "第三方服务异常");

private final String code;
private final String message;
// constructor...
}

// 2. 定义异常类
public class BusinessException extends RuntimeException {
private final ErrorCode errorCode;

public BusinessException(ErrorCode errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode;
}

public BusinessException(ErrorCode errorCode, Object... args) {
super(String.format(errorCode.getMessage(), args));
this.errorCode = errorCode;
}
}

// 3. 服务层正确抛异常
@Service
public class OrderService {

public Order createOrder(CreateOrderRequest request) {
// 业务校验 - 用异常表示业务规则不满足
User user = userService.findById(request.getUserId())
.orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND, request.getUserId()));

// 余额校验
if (user.getBalance().compareTo(request.getAmount()) < 0) { throw new BusinessException(ErrorCode.INSUFFICIENT_BALANCE); } // 库存校验 Product product = productService.findById(request.getProductId()) .orElseThrow(() -> new BusinessException(ErrorCode.PRODUCT_NOT_FOUND));

if (product.getStock() < request.getQuantity()) { throw new BusinessException(ErrorCode.PRODUCT_OUT_OF_STOCK, product.getName()); } // 创建订单 try { Order order = Order.builder() .userId(user.getId()) .productId(product.getId()) .amount(request.getAmount()) .build(); return orderRepository.save(order); } catch (DataAccessException e) { // 数据库异常 - 包装成系统异常往上抛 throw new SystemException("创建订单失败", e); } } } // 4. 全局异常处理器 @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(BusinessException.class) public ResponseEntity handleBusinessException(BusinessException e) {
return ResponseEntity.badRequest().body(
ErrorResponse.builder()
.code(e.getErrorCode().getCode())
.message(e.getMessage())
.build()
);
}

@ExceptionHandler(SystemException.class)
public ResponseEntity handleSystemException(SystemException e) {
log.error("系统异常", e);
return ResponseEntity.status(500).body(
ErrorResponse.builder()
.code("SYSTEM_ERROR")
.message("系统繁忙,请稍后重试")
.build()
);
}
}
```

这样一套下来:
- 异常分类清晰
- 错误码统一管理
- 日志记录完整
- 上层处理明确

## 总结

异常处理不是简单的try-catch,它是一门设计艺术:

1. **区分业务异常和系统异常** - 不是所有问题都是Exception
2. **异常要往上抛** - 别在底层把异常吞了
3. **异常信息要完整** - 包含足够的上下文
4. **统一异常处理** - 用全局异常处理器集中处理
5. **异常要分类** - 用枚举管理错误码

记住一句话:**好的异常处理,是让调用者知道发生了什么,而不是隐藏问题。**

好了,今天的分享就到这里。你们代码里有哪些奇葩的异常处理?评论区让我开开眼!

咱们评论区见!🦞

(全文完)

相关文章

你的SQL是怎么把数据库玩死的:一个CRUD工程师的自我救赎
还在自己折腾服务器部署?这钱我替你省了!
API设计:别让你的接口成为别人的噩梦
我删掉了70%的代码后,系统反而更快了:一个过度设计受害者的自白
你的API为什么这么难用?小龙虾的接口设计避坑指南
一个字母引发的惨案:我是如何被null折磨了三天的

发布评论