
大家好我是晚安code。说到 Java编程规范很多同学第一反应是条条框框烦死了。但阿里《Java 开发手册》里的【强制】规约几乎每一条都是拿线上故障换来的以 1.7.1 黄山版、2022 年 2 月版本为准。这篇我把手册里最容易让项目翻车的 15 条【强制】规约筛出来按命名、日期数值、集合、并发、数据层五类整理成一份避坑清单每条都配反例和事故场景。建议先收藏下次 code review 前翻一遍。一、命名别让名字埋雷Java命名规范命名不只是好不好看的问题——名字起错框架解析都可能翻车。这套 Java 命名规范里有三条最致命坑 1Boolean 字段加 is 前缀。反例是private Boolean isDeletedgetter 自动生成isDeleted()序列化时框架反解析误以为字段叫deleted结果字段直接丢失前端拿到的 JSON 里永远少这一个。正确写法是private Boolean deleted。坑 2拼音和英文混排。反例DaZhePromotion打折、getPingfenByName()评分读代码跟猜谜一样。手册明确要求命名用完整英文单词组合纯拼音也不行。坑 3魔法值直接写死。有次我 debug 一个缓存查不到的 bug 查了一下午最后发现是两个 key 一个带了下划线一个没带——开发者 A 写Id#taobao_ tradeId开发者 B 复制时少了个下划线。这就是魔法值的代价// 反例魔法值直接写死复制时少个下划线就出事故StringkeyId#taobao_tradeId;// 正确定义成常量publicstaticfinalStringCACHE_KEY_PREFIXId#taobao_;魔法值Magic Number直接硬编码在代码里、没有任何说明的常量。就像卷子上随手写的中间结果只有出题人自己看得懂。二、日期与数值翻车率最高的两类Java日期格式化、Java浮点数精度日期和数值是 Java 里翻车率最高的两类而且坑都很安静——不报错就是结果不对。尤其是 Java 日期格式化跨年那几天必现。坑 4YYYY 和 yyyy 别混。小写 yyyy 是当天所在的年大写 YYYY 是当天所在周属于的年份。2017 年 12 月 31 日用YYYY-MM-dd格式化得到的是 2018-12-31线上日志和报表日期全线错位。正确写法是newSimpleDateFormat(yyyy-MM-dd HH:mm:ss)// 正确newSimpleDateFormat(YYYY-MM-dd HH:mm:ss)// 反例跨年必翻车坑 5SimpleDateFormat 线程不安全。这个类不是线程安全的别定义成 static 共享多线程下偶发解析错乱。JDK8 之后直接换DateTimeFormatter官方评价就是 immutable、thread-safe。坑 6浮点数别用 比较。二进制没法精确表示大部分小数1.0F - 0.9F和0.9F - 0.8F用比结果是 false。这就像用十进制写 1/3永远是 0.333… 写不完。要么给个误差范围要么用 BigDecimal// 反例if(ab){}// a1.0F-0.9Fb0.9F-0.8F结果为 false// 正确指定误差范围if(Math.abs(a-b)1e-6F){}坑 7BigDecimal 用 compareTo 不用 equals。equals 会连精度一起比new BigDecimal(1.0).equals(new BigDecimal(1.00))返回 falsecompareTo 忽略精度才是你想要的行为。另外别用new BigDecimal(0.1)double 入参本身就有精度损失要用字符串构造或valueOf。三、集合操作一半事故出在这Java集合遍历集合操作贡献了 Java 运行时异常的一大半而且大部分是同一个套路——边遍历边改。这一节的坑全是【强制】级坑 8foreach 里删元素。for-each 底层是 Iterator边遍历边list.remove轻则元素跳过重则直接ConcurrentModificationException。正确做法是用 Iterator 的 remove或 JDK8 的removeIf// 反例for(Stringitem:list){if(1.equals(item))list.remove(item);}// 正确list.removeIf(item-1.equals(item));坑 9Integer 别用 比较。-128 到 127 之间有IntegerCache复用对象这个区间用 碰巧没问题一旦超出每次都是新对象 比的是引用两个 128 直接不相等。统一用 equals 或Objects.equalsIntegera128;Integerb128;ab;// false别问问就是踩过a.equals(b);// true坑 10Arrays.asList 不能增删。它返回的是 Arrays 内部类add、remove、clear一律抛UnsupportedOperationException而且它只是数组的视图改数组或改 list 会互相影响。坑 11subList 不能强转 ArrayList。subList 返回的是内部类 SubList强转会抛 ClassCastException它是对原列表的视图之后你再动父集合子列表遍历就会 ConcurrentModificationException。四、并发线程池和锁的坑Java线程池并发场景下用 Executors 图省事是 Java 里性价比最高的自杀方式。坑 12别用 Executors 创建线程池。newFixedThreadPool的队列容量是Integer.MAX_VALUEnewCachedThreadPool的线程数上限也是Integer.MAX_VALUE——高并发一冲进来任务只进不出内存直接打爆 OOM。正确姿势是用ThreadPoolExecutor手写参数配有界队列和拒绝策略线程池Thread Pool预先创建一批线程复用来执行任务的技术。可以理解为「工地上随时待命的搬运工」。坑 13ThreadLocal 用完要清理。线程池里的线程会被复用ThreadLocal 存的值就像贴在公司工位上没撕的便签下一个任务照样看得见。轻则数据串线重则内存泄漏。规范要求用 try-finally 包起来最后remove()。五、POJO 与接口数据层的细节POJO 里用基本类型等于给 NPE 留了一扇随时会开的大门。坑 14POJO 属性必须用包装类型。数据库查出来可能为 null用基本类型int接收自动拆箱当场 NPERPC 失败返回 null用包装类型还能表达调用失败这个额外信息页面显示一个中划线而不是错误的 0%。所以规约是POJO 属性和 RPC 参数返回值用包装类型局部变量才用基本类型。POJOPlain Old Java Object普通 Java 对象通常指 DO、DTO、VO 这类承载数据的类。可以理解为「专门装数据的快递箱」。坑 15超大整数返回前端用 String。Java 的 Long 最大能到 2^63-1但 JavaScript 的 Number 只能精确表示 2^53 以内的整数。订单号 16 位以上直接丢精度——后端传362909601374617692前端收到362909601374617660前后端对不上账。有次同事调我接口前端说订单号对不上最后定位到就是这个原因。服务端一律用 String 返回这类超长 ID。可能有人会问这十几条规约全背下来不得累死不用背。优先级就按【强制】【推荐】【参考】先守住强制级的。剩下的交给工具IDEA 装个 Alibaba Java Coding Guidelines 插件写违规代码当场标红比人肉记清单靠谱得多。我这份清单也主要是为了让你明白每条规则背后的原因——懂了为什么规则自然就记住了。可能有人会问现在都用 LocalDateTime 了SimpleDateFormat 的坑还重要吗重要。老项目迁移不是一夜之间的事大量系统还在跑 Date SimpleDateFormat而且理解了它为什么线程不安全你才能真正理解新 API 的 immutable 设计好在哪。集合类Key 是否允许 nullValue 是否允许 null是否线程安全Hashtable不允许不允许是TreeMap不允许允许否ConcurrentHashMap不允许不允许是HashMap允许允许否看这张表就明白了很多人被 HashMap 带偏以为 ConcurrentHashMap 也能存 null实际存了直接 NPE。旧 API新 APIJDK8说明DateInstant时间戳CalendarLocalDateTime日期时间SimpleDateFormatDateTimeFormatter线程安全不可变说白了这套 Java编程规范里的强制规约不是给你添麻烦而是把别人花真金白银踩过的坑提前标了出来。写代码前多问一句这名字规范吗、这个比较能用 吗就能省下大半年排查线上故障的时间。我是晚安code持续分享编程干货。觉得有用的话记得点赞收藏和关注~也欢迎在评论区聊聊这 15 条规约里你亲自踩过哪一个踩的时候排查了多久