52 | 总结篇:Java 踩坑的 10 条黄金法则
摘要:52 篇文章、8 个辑、覆盖 Java 基础到生产实战。本文把最核心的 10 条法则提炼出来,方便你记忆和团队分享。
一、黄金法则(背诵版)
法则 1:对象比较,一律用equals
基本类型(int、long...)→ 用 == 引用类型比较"值相等" → 用 equals String 比较 → 永远 equals,别用 == 包装类型(Integer、Long...)→ equals 或先拆箱相关文章:第 01 篇
法则 2:浪费BigDecimal禁止用double构造#
new BigDecimal(0.1) → ❌ 精度已丢失 new BigDecimal("0.1") → ✅ 正确 BigDecimal.valueOf(0.1) → ✅ 正确(内部先转 String)相关文章:第 03 篇
法则 3:synchronized锁定的是对象,不是代码块#
String、Integer、Boolean 不要做锁对象(可能共享) 锁对象必须是 private final 的 缩小锁范围:只包住需要互斥的代码相关文章:第 16 篇
法则 4:i++不是原子操作,高并发用Atomic类#
i++ → 读 → 改 → 写(三步,不是原子) 低并发 → AtomicInteger 高并发 → LongAdder(Java 8+)相关文章:第 18 篇
法则 5:@Transactional自调用不生效,必须拆 Service#
同一个类里调用 @Transactional 方法 → ❌ 不生效 拆到另一个 Service → ✅ 经过代理 Exception 类型 → 必须指定 rollbackFor = Exception.class相关文章:第 32 篇
法则 6:集合框架,先想清楚再选#
List 查多改少 → ArrayList List 头插/头删多 → LinkedList Map 高频查找 → HashMap Map 需要排序 → TreeMap 并发 Map → ConcurrentHashMap相关文章:第 09 ~ 15 篇
法则 7:线程池,禁止用Executors创建#
✅ 用 ThreadPoolExecutor 手动创建 ✅ 队列必须有界(防止 OOM) ✅ IO 任务用独立线程池(别占满 commonPool) ✅ 提交任务必须处理异常(submit 后必须 get)相关文章:第 19、20、24 篇
法则 8:JVM 参数,不只设-Xmx#
-Xmx 只限制堆 Metaspace → -XX:MaxMetaspaceSize DirectBuffer → -XX:MaxDirectMemorySize 容器化 → -XX:+UseContainerSupport(JDK 8u191+)相关文章:第 29 篇
法则 9:MyBatis 能用#{}就不用${}#
#{} → ? 占位符,预编译,安全 ✅ ${} → 字符串替换,有注入风险 ❌ 动态表名用 ${} 时,必须白名单校验相关文章:第 41 篇
法则 10:生产问题,保留现场优先于重启#
OOM → -XX:+HeapDumpOnOutOfMemoryError(自动 dump) 死锁 → jstack <pid> > dump.txt(保留线程堆栈) 慢查询 → 开慢查询日志(slow_query_log) 重启前 → 一定先 dump 现场!相关文章:第 50、51 篇
二、记忆卡片(速查)#
第一辑:基础语法篇(8 篇)#
| 核心记忆点 | 一句话 |
|---|---|
| == vs equals | == 比地址,equals 比值 |
| 自动拆箱 | 包装类型运算前必须判空 |
| BigDecimal | 禁止用 double 构造 |
| String + | 循环内拼接必须用 StringBuilder |
| equals + hashCode | 必须同时重写,否则 HashMap 失效 |
| try-finally | finally 里别写 return |
| 泛型擦除 | 运行期拿不到泛型类型 |
| 枚举 ordinal | 别用,用自定义 code 字段 |
第三辑:并发编程篇(10 篇)#
| 核心记忆点 | 一句话 |
|---|---|
| synchronized | 锁定对象,不是代码块 |
| volatile | 保证可见性,不保证原子性 |
| i++ | 不是原子操作 |
| 线程池 submit | 异常被吞,必须 get() |
| 线程池队列 | 必须有界,防止 OOM |
| CountDownLatch | countDown() 必须放 finally |
| ThreadLocal | 用完必须 remove(),防止泄漏 |
| DCL 单例 | 必须加 volatile |
| CompletableFuture | 生产环境必须指定自定义线程池 |
| 锁选型 | 简单用 synchronized,高级用 ReentrantLock |
三、团队落地建议#
3.1 代码评审 Checklist#
把这份 Checklist 加入团队的 PR 模板:
Java 代码评审 Checklist(踩坑预防版) □ 1. 字符串比较是否用了 equals?(禁止 ==) □ 2. BigDecimal 是否用了 String 构造?(禁止 double 构造) □ 3. 包装类型参与运算前是否判空?(防止 NPE) □ 4. 重写 equals 是否同时重写了 hashCode? □ 5. ArrayList 循环删除是否用了 removeIf?(禁止 foreach 里 remove) □ 6. 线程安全类是否选对了?(ConcurrentHashMap vs HashMap) □ 7. 锁对象是否是 private final 的?(禁止 String/Boolean 做锁) □ 8. i++ 在并发场景下是否用了 AtomicInteger? □ 9. @Transactional 方法是否是 public 的?是否避免了自调用? □ 10. 线程池是否用 ThreadPoolExecutor 手动创建?(禁止 Executors) □ 11. 提交到线程池的任务内部是否有 try-catch? □ 12. ThreadLocal 是否在 finally 里 remove() 了? □ 13. @Async 方法是否加了 @EnableAsync?是否避免了自调用? □ 14. MyBatis 的 SQL 是否用了 #{}?(禁止用 ${} 拼 WHERE 条件) □ 15. 分页查询偏移量是否超过了 10000?(超过了要用延迟关联优化) □ 16. 生产环境 -Xmx 是否同时配了 MaxMetaspaceSize 和 MaxDirectMemorySize? □ 17. 日志打印对象时,toString() 是否可能递归? □ 18. 用 SimpleDateFormat 的地方是否线程安全?(建议迁移到 DateTimeFormatter) □ 19. 实现 Serializable 的类是否写了 serialVersionUID? □ 20. OOM 参数是否加了 -XX:+HeapDumpOnOutOfMemoryError?3.2 IDEA 必装插件#
| 插件 | 作用 |
|---|---|
| Alibaba Java Coding Guidelines | 扫描阿里开发手册规约 |
| SonarLint | 静态代码分析,提前发现 bug |
| MyBatisX | MyBatis XML 和 Mapper 互跳,警告 ${} 风险 |
| Lombok | 减少样板代码,自动生成 *ArgsConstructor |
3.3 新人入职培训#
把前 8 篇(基础语法篇)作为新人必读,要求:
- 通读 8 篇文章
- 每篇文章后面附 3 道选择题(自测)
- 排入新人入职第一周的培训计划
四、专栏使用建议#
4.1 作为团队知识库#
建议存放位置: - 腾讯文档(方便协作、搜索) - 公司内网 Wiki(Confluence / 飞书文档) - 个人博客(Hexo / Hugo,SEO 友好)4.2 持续更新#
Java 在演进,坑也在变化。建议:
- 每遇到一个新坑,写一篇新文章
- Java 新版本(每 6 个月一个 LTS)发布后,检查是否有新的踩坑场景
- 读者留言里的高频问题,补充到对应文章里
4.3 配套练习#
建议为每篇文章写一个「课后练习」(JUnit 测试),让读者运行测试时直观感受 bug:
// 第 01 篇配套练习@TestpublicvoidtestStringComparison(){Strings1=newString("hello");Strings2="hello";assertFalse(s1==s2);// 预期:== 比较失败assertTrue(s1.equals(s2));// 预期:equals 成功}五、结语#
踩坑不可怕,可怕的是在同一个坑里跌倒两次。
这份专栏的目标,是让你把「不知道有这个坑」变成「我知道这里有坑,绕过去」。
Java 还在演进,新的坑会不断出现。保持好奇心,保持对底层原理的追问,你就能在大部分坑出现之前就识别它们。
祝编码愉快,生产环境 0 Bug。🎉
全专栏完。52 篇文章,8 个辑,祝你踩坑越来越少。
附录:文章索引(快速查阅)#
| 辑 | 文章范围 | 核心主题 |
|---|---|---|
| 第一辑:基础语法篇 | 01 ~ 08 | == vs equals、BigDecimal、String+、枚举 ordinal |
| 第二辑:集合框架篇 | 09 ~ 15 | CME、HashMap 扩容、HashSet 可变对象、Stream 并行流 |
| 第三辑:并发编程篇 | 16 ~ 25 | synchronized、volatile、i++、线程池、ThreadLocal、DCL、CompletableFuture |
| 第四辑:JVM 与性能篇 | 26 ~ 31 | toString 递归、反射性能、finalize、OOM、Soft/Weak Reference、Lambda this |
| 第五辑:Spring/Spring Boot 篇 | 32 ~ 39 | @Transactional、Field 注入、@Async、@Value、循环依赖、AOP、配置加载 |
| 第六辑:数据库与 MyBatis 篇 | 40 ~ 44 | PreparedStatement null、#{} vs ${}、连接池、LIMIT 优化、保留字 |
| 第七辑:日常开发技巧篇 | 45 ~ 49 | 日志 toString、switch 穿透、SimpleDateFormat、Optional、serialVersionUID |
| 第八辑:综合实战篇 | 50 ~ 52 | OOM 排查、死锁定位、总结篇 |