《Java开发手册》泰山版要点精编

来源:阿里巴巴集团技术团队集体智慧结晶,v1.6.0 泰山版(2020-04-22),首次发布错误码统一方案。云盘文件虽存放在 qt/ 目录,内容与 Qt 无关,实为 Java 编码规约手册(57 页正文 + 附录)。

手册定位与结构

  • 愿景:“码出高效,码出质量”——规范不是消灭创造性,而是限制过度个性化、降低协同沟通成本
  • 七大维度:编程规约、异常日志、单元测试、安全规约、MySQL 数据库、工程结构、设计规约
  • 规约按约束力分三级:强制 / 推荐 / 参考;每条附”说明 / 正例 / 反例”
  • 配套:Java 开发规约 IDE 插件(P3C,下载 152 万人次)、《码出高效》图书(收入捐赠公益)

一、编程规约(高频踩坑点)

命名

  • 严禁拼音英文混合(DaZhePromotion ✗);类名 UpperCamelCase,DO/BO/DTO/VO/UID 例外
  • POJO 布尔变量禁止 is 前缀——框架反解析会误认属性名为 deleted,导致序列化错误(MySQL 字段反而必须 is_xxx,需 resultMap 映射)
  • Service/DAO 实现类用 Impl 后缀;领域模型 xxxDO/xxxDTO/xxxVO,禁止 xxxPOJO

常量与格式

  • 禁止魔法值(真实故障案例:缓存 key Id#taobao_ 少下划线导致线上事故)
  • 4 空格缩进禁 tab;单行 ≤120 字符;方法 ≤80 行;UTF-8 + Unix 换行

OOP 关键规约

  • Integer 值比较必须用 equals:-128~127 走 IntegerCache 复用对象,区间外在堆上新建,== 是大坑
  • 浮点等值判断:基本类型不能 ==,包装类型不能 equals;用误差范围或 BigDecimal
  • 禁止 new BigDecimal(double)(精度损失 0.1f→0.10000000149),用字符串构造或 valueOf
  • 货币金额以最小货币单位 + 整型存储
  • POJO 属性用包装类型、RPC 返回值参数用包装类型、局部变量用基本类型;POJO 不设默认值
  • 覆写方法必须加 @Override;equals 用常量调用(“test”.equals(obj))

日期时间

  • 年份用小写 yyyy(大写 YYYY 是”周边年份”,跨年周返回下一年)
  • 禁 java.sql.Date/Time/Timestamp;不写死一年 365 天(闰年越界);月份用枚举(Calendar 月度 0-based)

集合处理(最密集的雷区章节)

  • 重写 equals 必重写 hashCode;Map 作键的自定义对象必须覆写两者
  • Collectors.toMap() 必带 mergeFunction(重复 key 抛 IllegalStateException),value 为 null 抛 NPE
  • subList 是视图不是副本;对父集合增删导致子列表 ConcurrentModificationException
  • Arrays.asList() 返回适配器,不能 add/remove;Collections.emptyList() 是 immutable
  • foreach 中禁 remove/add,用 Iterator
  • toArray 用 toArray(new String[0])(长度 0 性能最好)
  • HashMap 初始化指定容量:initialCapacity = 元素数/0.75 + 1(1024 元素不设初值→7 次被迫扩容)
  • Map null 值表:HashMap 允许 K/V 为 null;ConcurrentHashMap 都不允许(NPE)
  • PECS 原则:<? extends T> 只读不能 add,<? super T> 只写不能 get

并发处理

  • 线程池禁用 Executors 创建(Fixed/Single 队列 Integer.MAX_VALUE→OOM;Cached 线程数 MAX_VALUE→OOM),用 ThreadPoolExecutor
  • SimpleDateFormat 线程不安全:ThreadLocal 包装或 JDK8 DateTimeFormatter
  • ThreadLocal 必须 try-finally remove()(线程池复用场景内存泄漏)
  • lock() 必须在 try 块之外、且 lock 与 try 之间无可抛异常调用;tryLock 先判断持有再 unlock
  • 多资源加锁保持一致顺序防死锁;锁粒度:无锁>锁区块>对象锁>类锁
  • 并发更新防丢失:冲突率 <20% 用乐观锁(version,重试 ≥3 次),资金敏感用悲观锁(一锁二判三更新四释放)
  • Timer 一个任务未捕获异常会终止其他任务 → 用 ScheduledExecutorService
  • 双重检查锁加 volatile;count++ 用 AtomicInteger / JDK8 LongAdder

控制语句

  • switch(String) 外部参数先判 null;必须有 default 放最后
  • 三目运算符自动拆箱 NPE:flag ? a*b : c,c 为 null 的 Integer 会拆箱抛 NPE
  • 高并发禁用”等于”判断作退出条件(会被击穿成负数),用区间判断
  • if-else 不超 3 层,用卫语句/策略/状态模式重构
  • 参数校验:低频/高开销/高稳定性/对外开放接口/敏感入口要校验;循环内高频/DAO 层/private 可省略

二、异常日志

错误码(泰山版新增,A/B/C 三段式)

  • 5 位字符串 = 来源 + 4 位编号:A=用户端错误,B=当前系统错误,C=第三方服务错误;正常返回 00000
  • 一级宏观码:A0001(用户端)、B0001(系统)、C0001(第三方)
  • 错误码不体现版本号/等级;不直接输出给用户(error_code / error_message / user_tip / stack_trace 各司其职)
  • 第三方错误 C 转 B 上抛时带上原错误码

异常处理

  • 可预检查的 RuntimeException(NPE、越界)不应用 catch 处理;异常不做流程控制
  • 事务中 catch 异常需注意手动回滚;finally 禁 return(丢弃 try 的返回点)
  • RPC/二方包/动态类调用必须 catch Throwable(二方包类冲突抛 NoSuchMethodError,非 Exception 子类)
  • NPE 六大场景:返回拆箱、DB 查询结果、集合元素、远程调用、Session、级联调用——JDK8 用 Optional
  • 错误返回策略:外部 http/api 用错误码;跨应用 RPC 用 Result(isSuccess+错误码+简短信息);应用内部用异常

日志规约

  • 依赖 SLF4J 门面而非 Log4j/Logback 实现 API
  • 日志保存 ≥15 天(异常有按周发生的频次);占位符 {} 代替拼接;trace/debug 先判 isDebugEnabled
  • 生产禁 System.out / e.printStackTrace();additivity=false 防重复打印
  • error 只记系统逻辑出错/异常;用户输入错误用 warn(避免频繁报警)
  • 禁直接用 JSON 工具序列化对象打日志(get 方法可能抛异常影响业务)

三、单元测试

  • AIR 原则:Automatic 自动化、Independent 独立、Repeatable 可重复(不依赖网络/中间件,用 Mock 注入)
  • BCDE 原则:Border 边界值、Correct 正确输入、Design 结合设计文档、Error 强制错误输入
  • 目标:语句覆盖率 70%,核心模块语句+分支覆盖 100%;单测代码必须在 src/test/java
  • 数据库测试:程序插数据、自动回滚或统一前缀标识;提测前完成,不建议发布后补

四、安全规约

  • 水平权限校验(防访问他人数据);敏感数据脱敏(137****0969)
  • SQL 参数绑定防注入(真实案例:# — 未转义导致 where 被注释掉全库更新);任何入参必须有效性验证(pageSize 过大 OOM、恶意 order by 慢查询、SSRF、ReDoS 等)
  • HTML 输出必须转义;AJAX 必须 CSRF 验证;URL 重定向白名单
  • 短信/邮件/支付等平台资源必须防重放(数量限制、疲劳度、验证码)

五、MySQL 数据库

建表

  • 是/否字段 is_xxx + unsigned tinyint;表/字段全小写,禁数字开头、禁复数、禁保留字
  • 索引命名 pk_/uk_/idx_ 前缀;小数用 decimal 禁 float/double;varchar >5000 改 text 独立表
  • 表必备三字段:id(bigint unsigned 自增主键)、gmt_create、gmt_modified
  • 分库分表门槛:单表 500 万行或 2GB,预计三年达不到就别提前分

索引

  • 业务唯一字段必须建唯一索引(墨菲定律:没有唯一索引必出脏数据)
  • 超三表禁 join;varchar 索引须指定长度(20 长度区分度通常 >90%)
  • 页面搜索严禁左模糊/全模糊(B-Tree 最左前缀),走搜索引擎
  • 覆盖索引避免回表;深分页用延迟关联:WHERE id IN (SELECT id ... LIMIT 100000,20)
  • explain 目标:consts > ref > range;组合索引区分度最高在最左,非等号条件会让右侧索引失效

SQL

  • count(*) 是 SQL92 标准(统计 NULL 行);count(col) 不统计 NULL;sum 全 NULL 返回 NULL 需 IFNULL 包裹
  • 禁外键与级联(分布式高并发不适用)、禁存储过程;订正数据先 select 再 update
  • 多表操作列必须加表别名限定(真实故障:两年后新表加同名字段导致 1052 ambiguous)
  • in 集合控制在 1000 内;utf8 vs utf8mb4(存储表情用后者)

ORM

  • 禁 select *;禁 resultClass,每表必须有 resultMap;参数用 #{} 禁 ${}(注入)
  • 禁 HashMap/Hashtable 作查询结果集输出(bigint 在不同 DB 版本解析成 Long/BigInteger 的线上故障)
  • 更新必须同步 gmt_modified;@Transactional 不滥用(考虑缓存/搜索/消息的补偿回滚)

六、工程结构

  • 分层:开放接口层 → Web → Service → Manager(通用能力下沉/第三方封装/多 DAO 组合)→ DAO
  • 分层异常处理:DAO catch(Exception) 转 DAOException 不打日志;Service 必须打日志带参数(保护案发现场);Web 不再上抛、跳友好页;开放接口层转错误码返回
  • 领域模型:DO(对表)/ DTO(传输)/ BO(业务)/ Query(>2 参数查询封装,禁 Map 传输)/ VO(展示)
  • 私服二方库:GAV 规则(com.BU.业务线);版本三段式,起始必须 1.0.0;线上禁依赖 SNAPSHOT(保证发布幂等);升级必须 dependency:resolve 比对仲裁结果
  • 二方库返回值禁枚举类型;禁子项目同 GAV 不同 Version
  • 服务器调优:tcp_fin_timeout=30、调大 max fd(默认1024,高并发 open too many files)、-XX:+HeapDumpOnOutOfMemoryError、Xms=Xmx

七、设计规约

  • 存储方案和底层数据结构必须评审通过并沉淀文档(生产执行后 double check)
  • 图形化表达门槛:User>1 类且 UC>5 用用例图;状态>3 用状态图;调用链对象>3 用时序图;模型类>5 用类图;≥2 对象协作复杂流程用活动图
  • 架构设计四目标:系统边界、模块关系、演化原则、非功能需求(安全/可用/扩展)
  • 设计原则:单一职责;慎用继承(符合里氏代换)优先聚合/组合;依赖倒置;开闭原则;DRY
  • “敏捷 ≠ 讲故事+编码+发布”,关键设计的文档沉淀不可省
  • 可扩展性本质 = 找到变化点并隔离变化点(众多设计模式的本质)
  • 代码即文档是错的:代码只是文档片段,纵深调用/依赖网/非功能需求需文档呈现

版本演进史(有意思的档案)

2016.12 试读版 → 2017.02 v1.0 正式版(阿里发布)→ 2017.05 完美版 → 2017.09 终极版(+单元测试)→ 2018.05 详尽版(+设计规约)→ 2019.06 华山版(移除”阿里巴巴”前缀,+21 条)→ 2020.04 泰山版(+34 条,错误码方案)

可行动点

  • 给自己的项目引入 P3C IDE 插件做静态规约扫描
  • 错误码 A/B/C 三段式可直接借鉴到自研服务的错误码体系设计
  • MySQL 索引/分表门槛(500 万行 / 2GB)是实用的架构决策基线
  • 医疗器械嵌入式 Linux 端虽非 Java 栈,但命名/评审/图形化规约(如状态图门槛)可平移到 C++/Qt 项目

关联