# 《Java开发手册(黄山版)》知识点全解 > AI生成|2026年10月29日|codebuddy|原书:guanbao yang《Java开发手册(黄山版)》,— > **我的阅读**:进度 0%|累计 1小时14分钟|开始 2024-06-14|最近 2024-06-14|读完 2024-06-14 ## 一句话总结 它把「一个 Java 团队到底该怎么写代码」拆成可判定的条目:命名、集合、并发、异常、日志、测试、安全、数据库、分层、设计,用「强制/推荐/参考」三级约束把个人习惯变成团队共识,并让这些条目能被静态检查工具自动守住。 ## 适合谁 / 前置知识 **适合**:已经能写能跑 Java 代码、但要带人、要评审、要把项目交给别人维护的工程师;后端团队的技术负责人;需要与 Java 后端对接的前端、测试、DBA;准备面试但希望答得有工程味的人。 **不适合**:零基础学语法的人(手册不讲语法,只讲取舍);只写一次性脚本、不维护的人;把手册当法律条文逐条背诵的人——它给的是默认值,不是唯一解。 **前置**:Java 8 及以上(Lambda、Stream、`java.time`);了解 Maven/Gradle;用过至少一个 Web 框架与关系型数据库;知道什么是线程池、索引、事务。缺这些也能读,但只能记住结论,理解不了代价。 --- ## 一、全书地图 原书按「写代码 → 出错 → 验证 → 防攻击 → 存数据 → 组织工程 → 设计」的顺序展开,七块内容构成一条从微观到宏观的链条。下表按主题分块,不标章节号(原书条目编号未在目录中提供,待核对)。 | 板块 | 管什么 | 回答的问题 | 与前后板块的关系 | |---|---|---|---| | 编程规约 | 命名、常量、格式、OOP、日期、集合、并发、控制语句、注释、前后端 | 一行代码怎么写才算合格 | 全书的原子层,后面所有板块都建立在这些写法上 | | 异常日志 | 错误码、异常处理、日志 | 出错时怎么表达、怎么留痕 | 是编程规约中「可观测性」的展开 | | 单元测试 | 测试粒度、AIR/BCDE、覆盖率 | 怎么证明代码是对的 | 把「写得好」变成「可验证」 | | 安全规约 | 注入、XSS、越权、脱敏、CSRF | 怎么不被攻击 | 横切关注点,约束前面所有层的输入输出 | | MySQL 数据库 | 建表、索引、SQL、ORM | 数据怎么存、怎么查才不塌 | 承接业务代码,是性能问题的高发区 | | 工程结构 | 应用分层、二方库、服务器 | 代码怎么摆放、依赖怎么管 | 从单个类上升到项目与交付 | | 设计规约 | 抽象、解耦、扩展、领域模型 | 需求变化时怎么不推倒重来 | 顶层收束,解释前面规约背后的设计动机 | **读法建议**:第一遍按上表顺序通读,建立索引;第二遍只读与自己项目痛点相关的板块(多数团队是并发、日志、MySQL);第三遍把它改写成团队的 Checkstyle/PMD/Sonar 规则集——只有落到工具里,手册才会真正生效。 --- ## 二、核心知识点 ### 1. 命名是成本最低的文档 **是什么**:类用 UpperCamelCase,方法/变量用 lowerCamelCase,常量全大写下划线分隔,包名全小写单数;布尔字段不加 `is` 前缀(避免序列化框架把 `isSuccess` 映射成 `success` 后取值错位);缩写词统一大小写(`userId` 而非 `userID`)。 **为什么重要**:名字是阅读时出现频率最高的信息,命名混乱会让每次读代码都重新解码。命名还是可检索性的基础——`getUserById` 能被搜到,`get` 不能。 **怎么用**:定义「领域词典」,把 User/Order/Payment 这类核心词固定下来,禁止同义混用(user/member/customer 三选一)。接口实现类用 `Impl` 后缀或 `Default` 前缀,二选一后全项目统一。 **常见误区**:把命名规约当成审美问题。它实际影响序列化、反射、ORM 映射和日志检索——是正确性问题,不只是好看问题。 ### 2. 魔法值必须常量化 **是什么**:代码里直接出现的数字、字符串字面量(`if (status == 3)`、`"SUCCESS"`)称为魔法值,应提取为有名字的常量或枚举。 **为什么重要**:魔法值把「含义」丢在了作者脑子里。改一个状态码要全库搜索替换,漏一处就是线上事故;而且编译器不会提醒你 `3` 和 `"3"` 不是一回事。 **怎么用**:状态、类型、开关一律用枚举,枚举带 `code` 与 `desc`,对外只暴露 code;跨模块共享的常量放在二方库的 `constant` 包,不要散落在各服务的 `util` 里。 **常见误区**:用 `String` 常量代替枚举,结果 `switch` 里全是字符串比较,拼写错误编译期发现不了。另一个误区是把常量类当垃圾桶,什么都往里塞,最后变成隐形的全局变量。 ### 3. OOP 规约:相等性、包装类与浮点 **是什么**:`equals` 与 `hashCode` 必须同时重写;包装类对象比较用 `equals` 而非 `==`;金额禁用 `double`/`float`,用 `BigDecimal`,且用字符串构造。 **为什么重要**:`Integer` 在 -128~127 之间有缓存,`==` 有时为真有时为假,是最经典的「本地能跑、线上偶发」bug;`0.1 + 0.2 != 0.3` 是二进制浮点的固有缺陷,用在金额上就是账对不平。 **怎么用**:`new BigDecimal("0.1")` 而不是 `new BigDecimal(0.1)`;除法必须指定精度和舍入模式(`RoundingMode.HALF_UP`);比较用 `compareTo` 而非 `equals`(`1.0` 与 `1.00` 的 `equals` 为 false)。POJO 一律重写 `toString`,日志排查时能省半小时。 **常见误区**:以为 `BigDecimal` 就安全,却用了 `equals` 比较或在 `divide` 时不给精度,直接抛 `ArithmeticException`。 ### 4. 日期时间:不要共用格式化器 **是什么**:`SimpleDateFormat` 是可变、非线程安全的,不能作为静态共享字段;应改用 `java.time` 的 `DateTimeFormatter`(不可变、线程安全)。日期用 `LocalDate`,时间戳用 `Instant`,跨时区显式传 `ZoneId`。 **为什么重要**:静态 `SimpleDateFormat` 在高并发下会解析出错误日期甚至抛异常,而且这种 bug 只在压测或大促时出现,极难复现。 **怎么用**:`DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")` 提为 `static final`;数据库存 `DATETIME` 或带时区的 `TIMESTAMP`,不要存 `yyyy-MM-dd` 字符串;对外接口统一用时间戳或 ISO-8601,避免「本地时间」歧义。 **常见误区**:用 `yyyy` 与 `YYYY` 混写——后者是「周所属年份」,12 月 31 日可能被格式化成下一年。这个坑每年年底都会有人踩。 ### 5. 集合:初始化容量与不可变视图 **是什么**:`ArrayList` 预设容量避免扩容拷贝;`Arrays.asList` 返回的是固定长度视图,不能 `add`;`subList` 返回的是原列表视图,原列表结构改变会导致 `ConcurrentModificationException`;遍历中删除元素要用 `Iterator.remove` 或 `removeIf`。 **为什么重要**:集合是业务代码里最密集的数据结构。容量估算错误带来反复扩容与 GC 压力;视图与原集合共享底层数组,容易产生「改了 A 结果 B 也变了」的隐蔽耦合。 **怎么用**:已知规模时 `new ArrayList<>(expectedSize)`;需要可变列表用 `new ArrayList<>(Arrays.asList(...))`;对外返回集合时用 `Collections.unmodifiableList` 或 `List.copyOf` 做防御性拷贝;`HashMap` 预设容量按「元素数 / 0.75 + 1」估算。 **常见误区**:以为 `Collections.unmodifiableList` 做了深拷贝——它只挡住结构修改,元素本身仍可变;返回内部集合而不拷贝,等于把封装撕开一个口子。 ### 6. 并发:线程池必须自己建 **是什么**:禁止用 `Executors` 的快捷工厂方法创建线程池(`newFixedThreadPool` 用无界队列、`newCachedThreadPool` 最大线程数为 `Integer.MAX_VALUE`),应显式 `new ThreadPoolExecutor` 并指定核心数、最大数、队列、拒绝策略、线程工厂(带业务名)。 **为什么重要**:无界队列在流量突增时会把任务堆到 OOM;无上限线程会打爆 CPU 和内存。线程名是线上排障的第一线索,`pool-1-thread-3` 毫无信息量。 **怎么用**:CPU 密集型核心数≈核数+1,IO 密集型可放大到核数的数倍;队列有界并配 `CallerRunsPolicy` 做背压;`ThreadLocal` 用完必须在 `finally` 里 `remove()`,线程池复用线程会让值泄漏到下个请求。 **常见误区**:只调参数不做隔离——核心业务与边缘业务共用一个池,边缘任务打满后核心业务一起饿死。应按业务域拆池并分别监控活跃线程数与队列长度。 ### 7. 控制语句:卫语句与参数校验 **是什么**:方法入口先做参数校验并快速返回(卫语句),把主逻辑放在最外层缩进;`switch` 必须有 `default`;避免超过三层的 `if-else` 嵌套;不在条件表达式里做赋值或复杂计算。 **为什么重要**:嵌套是认知负荷的主要来源,每加一层缩进,读者要在脑中多维持一个前提。参数校验前移能把错误挡在业务逻辑之外,避免半个事务已执行。 **怎么用**:校验用 `if (x == null) return/throw` 提前退出;复杂分支用策略模式或枚举分派替掉长 `if-else`;循环里避免 `try-catch`,把异常处理提到循环外。 **常见误区**:把校验写成「信任调用方」,只在外层 Controller 校验,内部服务被别的入口调用时直接空指针。校验应贴着数据使用点,而不是贴着 HTTP 入口。 ### 8. 注释:解释为什么,不解释是什么 **是什么**:类、公共方法必须有 Javadoc(作者、日期、参数、返回值、异常);方法内部注释说明「意图与约束」,不复述代码;`TODO`/`FIXME` 必须带责任人与期限;注释随代码同步更新,过期注释等同于错误信息。 **为什么重要**:注释是唯一能承载「当时为什么这么选」的载体,git log 只能说改了什么,说不出为什么否掉了另一个方案。 **怎么用**:在复杂算法、非直觉的边界处理、临时兼容逻辑处写注释;用 `// 兼容 v1 客户端,2026Q2 下线` 这类带时间的注释,让后人敢删。 **常见误区**:用注释掩盖糟糕的命名(`// 获取用户` 配 `getU()`);或者反向操作——注释写得比代码还长,代码改了注释没改,读者开始不信任何注释。 ### 9. 异常:不吞、不滥、不裸抛 **是什么**:捕获异常必须处理或转换后抛出,禁止空 `catch`;不要在 `finally` 里 `return`(会吞掉异常);资源关闭用 try-with-resources;自定义异常继承 `RuntimeException` 并保留 cause;对外统一错误码,不把堆栈暴露给调用方。 **为什么重要**:空 `catch` 让故障静默,日志里一片祥和,用户侧已经 500。`finally` 中 `return` 会覆盖 try 中的返回值与异常,是最难查的语言陷阱之一。 **怎么用**:捕获后要么补偿、要么包装成业务异常并带上原始 cause(`new BizException("下单失败", e)`),要么记录后向上抛;区分「可重试」(网络超时)与「不可重试」(参数非法),前者重试、后者快速失败。 **常见误区**:`catch (Exception e)` 一把梭,把 `OutOfMemoryError` 之外的所有问题都当业务异常吞掉;或者用异常做流程控制,把正常的「未找到」也抛异常,性能与语义双输。 ### 10. 日志:结构化、有上下文、不泄密 **是什么**:用占位符 `log.info("order={} status={}", orderId, status)` 而非字符串拼接;日志必须带可追踪的上下文(traceId、用户标识、订单号);`error` 打堆栈、`warn` 打结论、`info` 打关键节点、`debug` 打细节;禁止打印身份证、手机号、密码、密钥。 **为什么重要**:日志是线上唯一的事后证据。拼接写法在 `debug` 关闭时仍在做字符串运算;没有 traceId 的日志在微服务里等于孤岛。敏感信息进日志就是数据泄露,合规上直接不合格。 **怎么用**:入口生成 traceId 放入 MDC,跨线程传递时显式复制;异常日志用 `log.error("msg", e)` 传异常对象而不是 `e.getMessage()`;大对象只打关键字段或摘要,避免日志把磁盘写满。 **常见误区**:在循环里打 `info`,一次批量任务刷出几十万行;或者用 `System.out.println` 代替日志框架,上线后无法分级、无法采集。 ### 11. 单元测试:AIR 与 BCDE **是什么**:单元测试应满足 AIR(Automatic 自动化、Independent 独立性、Repeatable 可重复),核心用例遵循 BCDE(Border 边界、Correct 正确、Design 设计、Error 错误);核心业务目标行覆盖率较高,但覆盖率不是目的;测试不依赖网络、数据库、第三方服务,外部依赖用 mock 或内存实现。 **为什么重要**:不可重复的测试会被团队关掉,关掉之后测试就从资产变成负债。覆盖率能造假——只测 getter 也能到 80%,但一个 bug 都拦不住。 **怎么用**:一个测试只验证一个行为,命名写清「场景_输入_期望」;用 `@BeforeEach` 造数据、`@AfterEach` 清理;断言用 `assertThrows` 验证异常类型与消息;把「曾经出过的线上 bug」逐条补成回归用例。 **常见误区**:为了覆盖率写「执行一遍不断言」的测试;或者测试之间共享静态状态,单跑通过、全量跑失败。 ### 12. 安全:所有外部输入都不可信 **是什么**:SQL 一律预编译(`PreparedStatement`/`#{}`),禁止字符串拼接;对输出做 HTML 转义防 XSS;每个涉及资源的接口都做水平与垂直越权校验;敏感数据落库加密、展示脱敏;写操作防 CSRF;文件上传校验类型、大小并重命名存储。 **为什么重要**:安全规约是唯一「做错一次就可能是事故」的板块。注入与越权常年位居 Web 漏洞前列,且往往一个接口没覆盖就全盘失守。 **怎么用**:`#{}` 用于参数,`${}` 只用于确定安全的表名/排序字段且需白名单校验;越权校验放在 Service 层,用「当前用户 + 资源 ID」一起查,而不是先查再比;脱敏在序列化层统一做,避免每个接口各写一遍。 **常见误区**:只在 Controller 做权限注解,Service 被内部任务或其他服务调用时绕过;或者以为前端校验够了——前端只是体验,后端才是边界。 ### 13. MySQL:建表、索引与 SQL 的三条底线 **是什么**:表必须有主键、必备三字段(主键、创建时间、更新时间);金额用 `DECIMAL`;`VARCHAR` 长度按实际需要;索引建在高区分度、出现在 `WHERE`/`ORDER BY` 的列上,遵循最左前缀;SQL 禁止 `SELECT *`、禁止大表无 `WHERE` 更新、禁止用 `count(列名)` 代替 `count(*)`;深分页用游标(`WHERE id > ?`)而非大 `OFFSET`。 **为什么重要**:数据库通常是系统最先崩的一层。缺主键让复制与在线变更变难;索引失效让查询从毫秒变秒级;`SELECT *` 让覆盖索引失效并放大网络传输。 **怎么用**:联合索引按「等值列在前、范围列在后」排列;用 `EXPLAIN` 看 `type`、`key`、`rows`,出现 `filesort`/`temporary` 就要警惕;单表行数过大时先考虑归档与分表,而不是无脑加索引;`NULL` 列不参与索引统计,重要查询列设 `NOT NULL DEFAULT`。 **常见误区**:给每个列都建单列索引,写入放大且优化器选错索引;在索引列上做函数运算或隐式类型转换(`varchar` 列传数字),索引直接失效。 ### 14. 工程结构:分层是依赖方向的规定 **是什么**:典型分层为「开放接口层 → 终端显示层 → Web 层 → Service 层 → Manager 层 → DAO 层」,依赖只能自上而下;Service 放业务逻辑,Manager 放对通用能力的封装(缓存、消息、第三方),DAO 只做数据访问;二方库依赖必须写明 GAV 与版本,生产禁用 SNAPSHOT。 **为什么重要**:分层的本质是「谁能依赖谁」的约束。没有约束时,DAO 被 Controller 直接调用、SQL 散落在各处,改表结构等于全项目改造。 **怎么用**:领域对象(DO/DTO/VO)按层隔离,禁止 DO 直接透传到前端;跨层调用只走接口;二方库版本用统一 BOM 管理,避免同一依赖多版本共存。 **常见误区**:为了「复用」让底层反向依赖上层,或把业务逻辑塞进 Manager 导致 Service 空心化。分层不是目录美观,是依赖方向的纪律。 ### 15. 设计规约:为变化留缝,但不预支变化 **是什么**:遵循单一职责、开闭原则、依赖倒置;接口设计「最小可用」,不暴露实现细节;避免过度设计与重复代码(DRY);领域模型区分实体、值对象、聚合;需求评审阶段就要识别可变点。 **为什么重要**:设计债的利息最高——前期省下的抽象,后期要用成倍的改动偿还。但过早抽象同样有害,错误的分层比没有分层更难拆。 **怎么用**:出现第三次重复再抽象(Rule of Three);用组合优于继承;扩展点用接口 + 策略注册表,而不是在核心流程里堆 `if`;每次加需求时问一句「这个变化是偶发还是趋势」。 **常见误区**:把设计模式当目标,五个类的功能拆出二十个接口;或者反之,用一个万能 `CommonService` 承载所有业务,最终无人敢改。 --- ## 三、关键概念速查表 | 概念 | 一句话解释 | 用在哪儿 | |---|---|---| | 魔法值 | 代码中无语义的字面量 | 常量/枚举提取,状态判断 | | `equals`/`hashCode` 契约 | 相等对象必须有相同哈希 | POJO、`HashMap` 键、去重 | | `BigDecimal` | 十进制精确运算类 | 金额、费率、对账 | | `DateTimeFormatter` | 不可变、线程安全的格式化器 | 替换静态 `SimpleDateFormat` | | `Arrays.asList` | 固定长度视图,非可变列表 | 快速构造只读参数 | | `subList` | 原列表的视图,非拷贝 | 分片处理,注意原列表变更 | | `ThreadPoolExecutor` | 可完全控制的线程池实现 | 替代 `Executors` 工厂方法 | | `ThreadLocal` | 线程私有变量 | 上下文传递,必须 `remove` | | 卫语句 | 提前返回,减少嵌套 | 参数校验、前置条件判断 | | try-with-resources | 自动关闭 `AutoCloseable` 资源 | 流、连接、文件 | | traceId | 贯穿一次请求的追踪标识 | 日志检索、链路排查 | | AIR / BCDE | 单元测试的六条判据 | 设计测试用例 | | 预编译 SQL | 参数与语句分离,防注入 | 所有带参数的查询 | | 最左前缀 | 联合索引按定义顺序匹配 | 索引设计与 SQL 改写 | | 应用分层 | 依赖只能自上而下的结构约束 | 包结构、模块划分 | | 二方库 | 团队间复用的内部依赖 | GAV 管理、版本对齐 | --- ## 四、可复用的方法/框架 **A. 代码评审四问(把规约变成评审动作)** 1. 这段代码的输入是否被校验,失败路径是否有明确返回? 2. 有没有魔法值、重复逻辑、超三层嵌套? 3. 出错时日志能否定位到「谁、在哪、为什么」? 4. 有没有并发共享状态、资源未关闭、外部输入直连 SQL/HTML? **B. 规约落地四步(团队级迁移路径)** 1. **选**:从手册里挑 20 条与当前事故最相关的条目,不求全。 2. **写**:改写成团队规范文档,每条附「反例 + 正例 + 检查方式」。 3. **卡**:配置 Checkstyle/PMD/Sonar 与 CI 门禁,新代码不达标不合入。 4. **盘**:每月看一次告警 Top 与线上事故,回写规则,淘汰无效条目。 **C. 索引设计决策清单** 区分度是否足够(唯一值比例)→ 是否出现在等值条件 → 是否有范围/排序 → 能否被覆盖索引命中 → 写入代价是否可接受 → `EXPLAIN` 是否走对索引。 **D. 并发安全自查** 共享可变状态在哪 → 是否有可见性问题(`volatile`/锁)→ 加锁顺序是否全局一致 → 线程池是否有界、是否隔离 → `ThreadLocal` 是否清理 → 是否用了线程安全的集合。 **E. 异常处理判定树** 能恢复吗?能→补偿并记录;不能→包装成业务异常上抛并保留 cause。是编程错误吗?是→快速失败,不要吞。是外部依赖故障吗?→超时+重试+熔断,并降级。 --- ## 五、我的阅读轨迹 | 项目 | 数据 | |---|---| | 阅读进度 | 0% | | 累计阅读 | 1小时14分钟 | | 开始阅读 | 2024-06-14 | | 最近阅读 | 2024-06-14 | | 读完时间 | 2024-06-14 | 进度显示 0%、但已有 1 小时 14 分钟记录,且开始、最近、读完三个日期同为 2024-06-14——说明这是一次「翻过但未系统读完」的阅读,数据口径可能未同步(待核对)。**复习建议**:不要从头顺读,按事故率排序攻三个板块——并发处理(线程池与 `ThreadLocal`)、异常与日志(可观测性)、MySQL(索引与深分页);这三块覆盖了多数线上问题。其次补安全规约与单元测试,最后用工程结构与设计规约收口。每读完一块,立刻在本项目里搜一遍反例,改一处、记一条,比读三遍有效。 --- ## 六、行动清单 1. 把项目中所有 `Executors.newXxx` 替换为显式 `ThreadPoolExecutor`,并给线程池命名。 2. 全库搜索 `new BigDecimal(` 的 double 构造与 `divide(` 无精度调用,逐一修正。 3. 搜索静态 `SimpleDateFormat` 字段,迁移到 `DateTimeFormatter`。 4. 给日志接入 traceId(MDC),并排查日志中的手机号、身份证、密钥输出。 5. 用 `EXPLAIN` 过一遍慢查询,重点看深分页与索引失效(函数、隐式转换)。 6. 在 CI 中接入 Checkstyle/PMD,先只对新增代码生效,避免一次性几千条告警。 7. 为近半年三个线上 bug 各补一条回归单测,验证测试能复现原问题。 8. 按「代码评审四问」改造团队的 MR 模板,把规约变成可勾选项。 --- ## 七、延伸阅读 1. **《Effective Java》(Joshua Bloch)**——先读这本。它解释「为什么」:为什么 `equals` 要与 `hashCode` 同时重写、为什么优先组合而非继承。手册给结论,这本书给推导,二者互为注解。 2. **《重构:改善既有代码的设计》(Martin Fowler)**——第二本。手册教你「新代码怎么写对」,重构教你「旧代码怎么安全地改过去」,配合使用才能落地到存量项目。 3. **《高性能 MySQL》**——第三本。手册的数据库板块只有条目,这本书补齐执行计划、索引结构、锁与事务的底层机制,适合在遇到真实慢查询后再读。 **顺序逻辑**:规范(是什么)→ 原理(为什么)→ 改造手法(怎么改)→ 数据层深挖(改到哪一层)。若时间有限,只读第 1 本的第 2、3、8、10 章与手册的并发、集合两节,收益最高。