大学生学习记录,如有错误或不足,欢迎指出,感谢!
项目开发类
主要分情况,一般是系统异常或者业务异常。
系统异常(数据库/Redis/网络/空指针等未知错误)
必须:log.error(关键信息,异常 e) + 抛出 自定义系统异常
- 一定要把异常堆栈 e 打出来,不然上线出问题查不到原因
- 抛一个有名字的异常(如SystemException),方便全局识别
系统异常写法
@Service
public class OrderService {
private static final Logger log = LoggerFactory.getLogger(OrderService.class);
public Order getOrder(Long orderId) {
try {
// 数据库操作,可能抛SQL异常/空指针
return orderMapper.selectById(orderId);
} catch (Exception e) {
// ✅ 必须:打印错误日志 + 带上完整异常堆栈e
log.error("查询订单异常,orderId:{}", orderId, e);
// ✅ 然后抛一个有名字的自定义异常
throw new SystemException("查询订单失败,请稍后再试");
}
}
}
RuntimeException 和 throw e
| 行为 | 核心特点 | 使用场景 |
|---|---|---|
throw e; + throws 异常 | 受检异常,编译器强制处理,调用链层层传递 | 业务可预期、调用方必须处理;原生 IO/JDBC 接口;对外 SDK |
throw new RuntimeException(e) | 转为运行时异常,无编译约束,代码简洁 | 系统故障、工具类、服务内部、配合全局异常处理器 |
- 能修复、要分支逻辑 → 直接抛原受检异常,加 throws
- 修不了、只是报错告警 → 包装成 RuntimeException 抛出
业务异常(参数错误/订单不存在/库存不足等预期内错误)
只抛自定义业务异常,不打 log.error
- 业务异常是正常流程里的判断,不是bug,不用打堆栈
- 全局异常处理器里打个
log.warn提示就行,避免日志泛滥。
public Order createOrder(Order order) {
// 业务判断,预期内的错误
if (order.getUserId() == null) {
// ✅ 只抛业务异常,不打log.error!
throw new BizException("用户ID不能为空");
}
if (order.getProductCount() <= 0) {
throw new BizException("商品数量必须大于0");
}
return orderMapper.insert(order);
}
为什么业务异常不打log.error?
- 业务异常是正常逻辑,不是程序bug
- 如果你每个校验都打
error, 日志文件会瞬间爆炸 - 全局异常处理器打个
warn就够了
全局异常处理类写法
一般controller 不捕获异常,交给这里的带有@RestControllerAdvice注解异常处理类。
// 全局异常处理类(整个项目只写这1次)
@RestControllerAdvice
public class GlobalExceptionHandler {
// 捕获【业务异常】
@ExceptionHandler(BizException.class)
public Result<Void> handleBizException(BizException e) {
// 日志只在这里打一次
log.warn("业务异常:{}", e.getMessage());
// 统一返回前端
return Result.fail(e.getMessage());
}
// 捕获【所有系统异常】
@ExceptionHandler(Exception.class)
public Result<Void> handleException(Exception e) {
log.error("系统异常", e);
return Result.fail("服务器繁忙,请稍后再试");
}
}
注意的细节点
- log.error 必须带上 e
java
运行
// 错误(没有堆栈,上线等于没日志)
log.error("查询订单失败");
// 正确(带完整异常堆栈,能定位代码行)
log.error("查询订单失败,orderId:{}", orderId, e);
- **抛的异常必须 “有名字”**不要抛
Exception或RuntimeException,要抛自定义的:
SystemException(系统错误)BizException(业务错误)全局处理器才能分开处理,返回不同提示。
基础语法模块
static final 和 final 的选择
使用 static final的场景
- 类级别的全局常量。比如数学常数Math.PI,或者最大次数,即不变的一些值。
- 所有实例共享且不可变。比如默认配置,单例对象。
- 好处:节省内存,所有实例共享;语义清晰,一眼就能看出是全局常量。
使用 final(不带 static)的场景
- 实例属性,每个对象必备,但值或引用不可变。
- 在构造对象时才能确定的值。比如你的
capacity,每个缓存实例容量可能不同,但一旦设好就不能再改。 - 对象自己持有的“零件” 。比如你的
lock和dummy节点,它们必须跟随实例,不能写成 static 让所有实例共用一把锁(那会完全破坏并发安全)。 - 好处:保证对象在构造完成后就不再被修改,从而实现不可变对象的安全发布和线程安全。