本文目录导读:

在Java案例复盘(特别是团队内部的技术复盘、代码审查或面试复盘)中,提到的“技战术短板”通常不是指某个具体的语法错误,而是指在面对复杂业务或高并发场景下,“经验不足”与“思维定势”导致的问题。
如果要把这些短板梳理清楚,可以归结为以下五个维度的“重灾区”:
并发与线程安全(“锁”的误用)
这是最容易暴露短板的地方,也是最经典的技术考点。
- 锁粒度过粗:为了图省事,直接给整个方法或大段代码加
synchronized或Lock,导致系统吞吐量急剧下降,甚至引发死锁,复盘时往往发现,其实只需要锁住共享变量那一行代码。 - 并发容器使用不当:以为用了
ConcurrentHashMap就万事大吉,却在复合操作(如“先查后改”或putIfAbsent与get之间的业务逻辑)上没有加锁,导致数据不一致,这是典型的“知道API,但不懂语义”的短板。 - 可见性问题:忽略了
volatile的适用场景(只保证可见性,不保证原子性),或者错误地认为加了volatile就能解决所有并发问题。
性能与调优(“索引”与“内存”的忽视)
业务跑得通不代表技术过关,性能瓶颈往往是复盘的重头戏。
- SQL与索引陷阱:在Java中使用ORM(如MyBatis或Hibernate)时,没有关注生成的SQL是否能走索引,常见短板是在数据库查询条件前加了隐式转换、对索引列使用了函数运算,或者用了“前模糊”查询(
LIKE '%xx'),导致索引失效,全表扫描拖垮数据库。 - JVM参数与内存泄漏:遇到OOM(内存溢出),第一反应是加堆内存,而不是通过
jmap或MAT分析对象存活率,短板在于没有排查是否存在大对象未释放、静态集合容量无限增长(伪内存泄漏)或InputStream未关闭等低级但致命的问题。 - 频繁的GC(垃圾回收):复盘时发现代码在循环体中创建了大量短生命周期对象,导致年轻代频繁触发Minor GC,不仅慢,而且造成CPU使用率极高,这反映了对对象复用和逃逸分析的理解不足。
事务管理(“范围”与“失效”)
交易系统中最怕的就是事务问题。
- 事务粒度过大:在
@Transactional方法中执行了耗时的网络调用或大量不相关的无关数据库操作,导致数据库连接长期被占用,连接池被耗尽。 - 事务自调用失效:在同一个类中,
this调用了另一个带@Transactional的方法,因为Spring AOP基于代理机制,事务直接失效,这是非常经典且隐蔽的短板。 - 异常捕获导致回滚失败:在方法内部用
try-catch吞掉了异常,且未抛出RuntimeException,导致事务无法回滚,产生脏数据。
代码设计与架构(“面向过程”的回归)
- 上帝类/长方法:类中塞满了各种不相关的功能,方法体动辄几百行,严重依赖
if-else和临时的Map传参,复盘时常常发现,本应使用策略模式或状态模式的地方,被写成了面向过程的流程代码,导致后续需求难以扩展。 - 缺乏防御性编程:对下游接口返回的数据不校验,直接
get或强转,导致NullPointerException或ClassCastException在线上偶发,非常被动。 - 调用链冗长:上下游调用之间同步阻塞,没有使用异步化(如MQ或CompletableFuture)来剥离非核心链路,导致接口RT(响应时间)极长,拖垮整体性能。
资源管理与I/O
- 连接泄漏:手动获取数据库连接、Redis连接或HttpClient连接,却因为异常分支忘了在
finally中关闭,或者没有使用try-with-resources,导致连接池耗尽。 - 非阻塞阻塞用:在使用 Netty 或 WebFlux 等非阻塞框架时,在事件循环线程里执行了阻塞操作(如强行走JDBC或Thread.sleep),导致整个事件循环卡死,线程被挂起,这是架构层面的隐形杀手。
复盘的终极短板
如果把这些具体问题抽象成一句话,最大的技战术短板其实是“缺乏底层原理与业务场景的映射能力”。
- 遇到问题:只求“跑通”,不求“跑稳”。
- 排查问题:喜欢猜(重启、加日志),而不是通过观测工具(Arthas、jstack、监控大盘)定位。
- 解决问题:喜欢用“轮子”(到处拷代码),但因为不懂内部实现,导致水土不服。
给复盘的建议:在复盘报告中,不要只写“我们用了并发HashMap”或“我们加了索引”,建议写清楚:我为什么选这个方案?在当时的数据规模下,这个方案的瓶颈会出现在哪里?如果数据量再大十倍,引擎是否会崩溃? 这种系统性的逻辑推演能力,才是真正需要补的“技术战术”短板。