360 Secure SkillHub
360 安全技能中心

java-coding-guide-pro

面向 Java 日常编码的约定助手,按场景给出唯一推荐做法:判空与字符串、集合与 Stream、日期时间、文件 IO 与 HTTP/JSON、线程池并发、Bean 拷贝、加密哈希、异常日志、现代 Java 语法和 BigDecimal 金额运算,并标注 JDK 8~21 特性门控、依赖坐标与项目既有库去重原则,附红旗清单与自查清单。

baixuanzhu
baixuanzhu
热度 12.75
最近更新 2026-08-09 10:09:41
分类:开发依赖:读写文件应用场景:代码生成 / 代码审查 / 调试排错 / API 集成

安装技能

复制下面的话术发给你的 Agent,它会通过 secure-skills 检索并安装该技能。

执行命令安装此 skill: npx secure-skills-cli add java-coding-guide-pro
Metadata
name
java-coding-guide
description
Use when writing or reviewing Java code involving any of: 字符串判空/格式化、集合与 Stream、日期时间、文件 IO、HTTP 调用、JSON 序列化、线程池与并发、Bean 拷贝、加密哈希、异常处理、日志、金额/浮点精确运算、或现代 Java 语法(var/record/switch 表达式/sealed/虚拟线程)。 触发信号:手写判空、new SimpleDateFormat、手搓 MessageDigest、new Thread()、Executors.newXxx、log.error("x"+e) 字符串拼接、BeanUtils.copyProperties、subList 分页、Optional.get() 不判空、new Random() 算范围、double/float 算钱、new BigDecimal(double)、bd.equals(...)。 覆盖 JDK 8~21+,按版本标注特性门控。不适用:业务架构设计、框架选型、DDL、纯算法、前端代码。
version
1.0.0
last_verified
2026-07-14

Java 编码指南

面向日常 Java 开发的编码约定助手。每条规则含「✗ 禁止 → ✓ 推荐 → 为什么」。 问题优先、一域一默认、框架中立(不含 Spring)。基于 Hutool 5.8.x + 主流库(OkHttp3 / Jackson / MapStruct / Lombok / SLF4J),覆盖 JDK 8~21+。 必选依赖:Hutool(core 必需;crypto 按需)+ Lombok + OkHttp3 + Jackson + MapStruct + SLF4J(坐标见「依赖坐标」段;不含 Guava / commons-lang3)。

三条铁律(开工前先记住)

  1. 框架中立:所有推荐默认不依赖 Spring。本指南不出现任何 Spring 专属方案。
  2. 一域一默认:每个场景只给唯一推荐,不列举「或 A 或 B」制造决策疲劳。
  3. 版本门控:先用「JDK 版本策略」确认目标版本,高版本特性按门控使用——JDK 8 项目严禁 var/record/switch 表达式/文本块/sealed/虚拟线程。

选型哲学(三档,按场景而非按库)

原生 API 状态 选型
完善(设计良好、完整) 用 JDK 原生:java.time、Optional、Stream、CompletableFuture、ThreadPoolExecutor
低级易错(易踩坑) 用工具库封装:加密→Hutool SecureUtil;字符串→StrUtil;集合→CollUtil
原生缺失 用行业主流库:HTTP→OkHttp3;JSON→Jackson;Bean→MapStruct;BCrypt→hutool-crypto

反面:不要因为「熟悉」就保留旧 API+workaround(如 ThreadLocal<SimpleDateFormat>),优先用更优 API(DateUtil/java.time)。

JDK 版本策略(激活时先执行)

  1. 探测目标 JDK:读 pom.xml 的 maven.compiler.release / <source> / <target>,或 build.gradle 的 sourceCompatibility / targetCompatibility;读不到则主动问用户「目标 JDK 版本是?」。
  2. 三档语义:
    • JDK 8(下限):可用 Optional/Stream/java.time/CompletableFuture/Lambda/方法引用。禁用 var/record/switch 表达式/文本块/sealed/虚拟线程/Stream.toList()。
    • JDK 11–17:+ var(10)、JDK HttpClient(11)、switch 表达式(14)、文本块(15)、record(16)、sealed(17)、Stream.toList()(16)。
    • JDK 21+:+ 虚拟线程、switch 模式匹配、SequencedCollection、record 模式。
  3. 详细门控见 references/09-modern-java.md。

域 → 唯一默认(路由表)

场景信号 唯一默认 详见
判 null / Optional 取值 / a.equals(b) 防 NPE JDK Optional/Objects;Hutool StrUtil.isBlank/ObjectUtil.equal 01
字符串判空/格式化/截取/分隔/命名转换/拼接 Hutool StrUtil 01
集合判空/新建/分块/交并差 Hutool CollUtil 02
集合分组/转Map JDK Stream/Collectors(默认);链式组合用 CollStreamUtil 02
日期格式化/解析/加减/差值/时间戳 JDK java.time 优先;Hutool DateUtil 处理遗留 Date 03
文件读写/流拷贝/遍历 Hutool FileUtil/IoUtil 04
HTTP GET/POST 调用 OkHttp3 04
JSON 序列化/反序列化 Jackson ObjectMapper 04
线程池/异步/锁/虚拟线程 JDK ThreadPoolExecutor + CompletableFuture 05
Bean 属性拷贝/转Map/克隆 MapStruct(默认,编译期安全);toString 用 Lombok @ToString 06
MD5/SHA/对称加密/密码哈希 Hutool SecureUtil/DigestUtil/BCrypt/AES(均 hutool-crypto);Base64 编解码用 hutool-core 的 Base64 07
随机数 Hutool RandomUtil 08
异常链/根因/断言 Hutool ExceptionUtil/Assert 08
日志记录 SLF4J 门面 + Logback(版本按 JDK 门控) 08
现代 Java 语法(版本门控) 见 09-modern-java.md 09
金额/浮点/精确小数运算 JDK BigDecimal(禁 double/float 算钱) 10

强约束(生成代码前必须满足)

  1. 判空:字符串→StrUtil.isBlank;集合→CollUtil.isEmpty;禁 == null || .isEmpty() / .trim().isEmpty()。
  2. 相等防 NPE:ObjectUtil.equal(a,b) / StrUtil.equals(a,b) / 常量在前;禁 a.equals(b) 可能 NPE。
  3. 日期:禁 SimpleDateFormat 作共享/静态变量(线程不安全);禁 Calendar 手算(月从 0);用 DateUtil 或 java.time。
  4. Bean 拷贝:默认 MapStruct;仅无 annotation processor / 一次性临时拷贝退 BeanUtil.copyProperties;禁随手 Spring/Apache BeanUtils 且不确认源/目标顺序。
  5. 加密:禁手搓 MessageDigest(前导零丢失);用 SecureUtil.md5/sha256。密码禁无盐 MD5/SHA,用 BCrypt.hashpw(pwd)。
  6. 集合新建:CollUtil.newArrayList(a,b);禁 new ArrayList<>() 后逐个 add 仅初始化。
  7. 集合分块/分页:CollUtil.partition/page;禁 subList 手写(越界 + 视图耦合)。
  8. 集合分组/转Map:默认 JDK Collectors.groupingBy/toMap(链式组合用 CollStreamUtil);禁手写 for+computeIfAbsent。
  9. IO:FileUtil/IoUtil;禁手搓流且 close 不兜底(必须 try-with-resources)。
  10. HTTP:OkHttp3 Request.Builder + try-with-resources;禁手搓 HttpURLConnection。
  11. JSON:Jackson ObjectMapper(复用单例);禁手拼 JSON 字符串。
  12. 线程池:new ThreadPoolExecutor + 有界队列 + 明确拒绝策略;禁 new Thread().start();禁 Executors.newCachedThreadPool/newFixedThreadPool(无界队列 OOM 风险)。
  13. 日志:SLF4J 占位符 log.error("x={}, y={}", x, y);禁字符串拼接 log.error("x="+e)。异常对象作最后一个参数不占位。
  14. 随机数:RandomUtil.randomInt(min,max)([min,max) 半开);禁 new Random().nextInt() 手算范围。
  15. 现代语法:按目标 JDK 门控(见 09);JDK 8 项目禁 var/record/switch 表达式/文本块。

借口对照表(基线测试得来,每个都要正面拦截)

借口(Agent 可能这么想) 现实
「单个方法没必要引 Hutool,手搓 MessageDigest 一样」 手搓的 hex 转换极易丢前导零导致哈希碰撞;SecureUtil.md5 一行且零风险。Hutool 是团队统一标准,不是可选优化。
「warn 级别一直开着,字符串拼接 log.warn("x="+id) 差别可忽略」 拼接每次都执行(即使该日志被关闭);占位符 {} 只在真正输出时格式化。一致用占位符是团队约定,无例外。
「ThreadLocal<SimpleDateFormat> 已经解决线程安全,没必要换」 ThreadLocal 是 workaround,有内存泄漏风险、有线程池场景复用问题。DateUtil.format/DateTimeFormatter 本身线程安全,是更优 API。
「Stream toMap 一行搞定,不用 Hutool 也行」 对的——JDK Stream 完善时用 JDK 原生(本指南三档哲学第一档)。但分组/转Map 若需工具,用 CollStreamUtil,不要用不存在的 CollUtil.groupBy。
「BeanUtils.copyProperties 很方便,框架自带」 Spring/Apache 版源/目标顺序相反,记错会静默拷空;且不报错。MapStruct 编译期类型安全;或 Hutool BeanUtil(顺序固定 source,target)。
「Executors.newFixedThreadPool 写起来短」 其内部是无界队列,任务堆积 OOM。必须 new ThreadPoolExecutor + 有界队列 + 拒绝策略。
「JDK 8 项目偶尔用个 var 没事」 编译失败。JDK 8 不支持 var(10+)。版本门控无例外。

C-CHECK 安装决策(需要 Hutool 但项目未引入时触发)

何时触发:判定要用 StrUtil/CollUtil/SecureUtil 等,但读 pom.xml/build.gradle 未发现对应 cn.hutool:hutool-* 模块。

判断是否已引入:读构建文件;无法判断则按下方「模板 D」一并问用户。不要即兴发挥问法——照念下列标准话术,保证一致、一次问全、给出坐标与兜底。

标准话术(按场景照念,勿即兴)

模板 A · 只需 core(多数场景)

这里需要 {场景,如:判断字符串空白},团队规范推荐 Hutool 的 {StrUtil.isBlank}(一行、null 安全、避坑)。 检测到 pom.xml 未引入 hutool-core。 A) 引入 hutool-core(版本由全局 BOM 统一,见下)→ 我用 {StrUtil}。 B) 不引入 → 我手写实现(会保留必要守卫,但不符团队统一规范)。 选 A 还是 B?

模板 B · crypto 场景(任何 MD5/SHA/AES/密码哈希,需 crypto 模块,不是 core)

这里需要 {场景,如:文件 MD5 校验 / 密码哈希},推荐 Hutool 的 {SecureUtil.md5 / BCrypt.hashpw}——SecureUtil/DigestUtil/BCrypt/AES 全部在 hutool-crypto 模块(不是 core;只有 Base64 在 core)。 检测到项目未引入 hutool-crypto。 A) 引入 hutool-crypto(版本由全局 BOM 统一,见下)→ 我用 {SecureUtil.md5 / BCrypt.hashpw}。 B) 不引入 → 我手写 MessageDigest(hex 必须 %02x 补零);密码场景禁无盐/弱盐(彩虹表反查),强烈建议 A。

加密/哈希场景固定走模板 B(crypto 模块):SecureUtil/DigestUtil 都在 hutool-crypto(不是 core);密码哈希禁无盐 MD5/SHA,用 BCrypt.hashpw,SecureUtil.md5/sha256 仅用于非密码场景(校验和、指纹)。

模板 C · 用户拒绝后的兜底说明(必须说 + 代码加注释)

已按你的要求手写。注意 antipattern:{具体坑,如:MessageDigest 的 hex 必须用 %02x 补零,否则前导零丢失致碰撞}。 已在代码加注释标注这是受控降级,建议后续引入 Hutool 统一。

模板 D · 首次 C-CHECK 合并 JDK 探测(省往返)

两个前置确认:① 目标 JDK(8 / 11-17 / 21+)?② 项目根是否已全局引入 hutool BOM(团队工具标准,见下)? 确认后我按版本门控 + 工具规范给代码。

分支落地

  • 用户同意 → 给对应坐标(见下表;core 最小集,BCrypt 才需 crypto)+ 用库方法。
  • 用户拒绝 → 手写实现,必须走对应 reference 的「antipattern 提醒」+ 加注释(模板 C);不可静默降级。

依赖坐标(项目根 pom.xml 全局引入 BOM 一次,仅此一处带版本;模块化,禁 hutool-all):

模块 artifactId 用途
hutool-core(必需最小集) hutool-core StrUtil/CollUtil/CollStreamUtil/DateUtil/ObjectUtil/BeanUtil/Base64/FileUtil/IoUtil/RandomUtil/ExceptionUtil/Assert/HtmlUtil
hutool-crypto(按需) hutool-crypto SecureUtil/DigestUtil(MD5/SHA 摘要)、BCrypt(密码哈希)、AES(对称加密)

Maven(推荐 BOM:版本单一来源,模块不带 version):

<properties>
    <hutool.version>5.8.47</hutool.version>
</properties>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-bom</artifactId>
            <version>${hutool.version}</version>
            <type>pom</type>
            <scope>import</scope> <!-- 注意:import,不是常规 scope -->
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-core</artifactId>
        <!-- 不写 version,由 BOM 统一;需要 MD5/SHA/BCrypt/AES 再加 hutool-crypto -->
    </dependency>
</dependencies>

Gradle(platform 方式):

implementation platform('cn.hutool:hutool-bom:5.8.47')
implementation 'cn.hutool:hutool-core'
// implementation 'cn.hutool:hutool-crypto'  // MD5/SHA/BCrypt/AES 才需要

Hutool 5.8.x 全线适用(最新稳定版 5.8.47;本地 API 对照官方 javadoc 核实,5.8.x API 稳定)。JDK 8/11/17/21 均兼容。

必选依赖(非 Hutool 工具栈)

项目根 pom.xml 引入一次(Hutool 见上)。不引 Guava / commons-lang3(Hutool + Lombok 已覆盖常用能力)。版本可由父 BOM 统一;下列为推荐稳定版。

库 坐标 推荐版本 用途 详见
Lombok org.projectlombok:lombok 1.18.34(provided) @Data/@ToString/@Builder/@Slf4j 06/08
OkHttp3 com.squareup.okhttp3:okhttp 4.12.0 HTTP 调用 04
Jackson com.fasterxml.jackson.core:jackson-databind 2.17.1 JSON(ObjectMapper 复用单例) 04
MapStruct org.mapstruct:mapstruct 1.5.5.Final Bean 拷贝(编译期;无 annotation processor 时按 06 降级 BeanUtil) 06
SLF4J org.slf4j:slf4j-api 2.0.13(JDK 8 用 1.7.36) 日志门面;实现默认 Logback(JDK 11+ 1.5.38 / JDK 8 1.2.13,门控见 08) 08

annotation processor:Lombok 与 MapStruct 均需配置——Maven 在 maven-compiler-plugin 的 <annotationProcessorPaths> 加两者;Gradle 用 annotationProcessor(Lombok 另需 compileOnly,MapStruct 另需 implementation)。以上版本 JDK 8~21 兼容。

红旗清单(出现以下任一 → 立即停手改写)

  • ✗ == null || str.trim().isEmpty() → 改 StrUtil.isBlank
  • ✗ new SimpleDateFormat(...) 作字段/静态 → 改 DateUtil/java.time
  • ✗ a.equals(b) 且 a 可能 null → 改 ObjectUtil.equal / 常量在前
  • ✗ MessageDigest.getInstance(...) 手搓 → 改 SecureUtil.md5/sha256
  • ✗ log.error("..." + x) 或 log.error("...", e + msg) → 改占位符 {}
  • ✗ new Thread(...).start() → 改线程池
  • ✗ Executors.newCachedThreadPool()/newFixedThreadPool() → 改 new ThreadPoolExecutor + 有界队列
  • ✗ BeanUtils.copyProperties(...) 未确认版本/顺序 → 改 MapStruct / BeanUtil
  • ✗ list.subList(from, to) 分页 → 改 CollUtil.partition/page
  • ✗ 手拼 JSON 字符串 → 改 Jackson
  • ✗ JDK 8 项目出现 var/record/switch 表达式/文本块 → 按版本门控删除
  • ✗ Optional.get() 前无 isPresent/orXxx → 改 orElse/orElseThrow
  • ✗ e.getMessage() 不判空直接用 → 改 ExceptionUtil.getMessage(e)(null 安全)
  • ✗ double/float 算钱、new BigDecimal(double)、bd.equals(...)、裸 divide → 改 BigDecimal 规范(见 10)

自查清单(输出代码前逐项过)

  • 判空是否用了 StrUtil.isBlank / CollUtil.isEmpty?
  • 日期是否避免了 SimpleDateFormat 共享 / Calendar 手算?
  • 相等判断是否防 NPE?
  • 加密是否用了 SecureUtil(没手搓 MessageDigest)?密码是否用 BCrypt?
  • Bean 拷贝是否用了 MapStruct/BeanUtil(源/目标顺序正确)?
  • 集合分块用 CollUtil.partition;分组/转Map 默认 JDK Collectors(链式组合才用 CollStreamUtil)?
  • IO 是否 try-with-resources?文本是否显式 UTF-8?
  • HTTP 是否 OkHttp3?JSON 是否 Jackson(复用单例)?
  • 线程池是否 new ThreadPoolExecutor + 有界队列 + 拒绝策略?
  • 日志是否用 {} 占位符(无字符串拼接)?
  • 现代 Java 语法是否符合目标 JDK 门控?
  • 需要的 Hutool 模块是否已引入(否则触发 C-CHECK)?
  • 金额/精确小数是否用了 BigDecimal(禁 double 算钱)?除法是否给了 scale+RoundingMode?比较是否用 compareTo?

使用流程

  1. 确认适用 + 探测目标 JDK(读构建文件或问用户)。
  2. 定位 reference:查「域→唯一默认」路由表,读对应文件。
  3. 编码遵循强约束 + 参考 reference 的 ✗→✓→为什么。
  4. 遇未引入 Hutool → 触发 C-CHECK。
  5. 输出前过自查清单 + 红旗清单。

版本与范围

  • Hutool 5.8.x(最新稳定 5.8.47,API 对照官方 javadoc 核实);Lombok 1.18.34;OkHttp3 4.12.0;Jackson 2.17.1;MapStruct 1.5.5.Final;SLF4J 2.0.13(具体坐标见「必选依赖」段)。
  • JDK 8 为下限,特性按 references/09-modern-java.md 门控。
  • 团队约定(新项目):禁 hutool-all(模块化引入);不引 commons-lang3(Hutool 覆盖);不引 Guava(命名线程工厂等手写,见 05);不引 Spring 专属工具。
  • 遗留项目去重原则(构建文件已存在某库时):
    • 已引 commons-lang3:用其 StringUtils/ObjectUtils/Validate 等,不要再为同类能力引 Hutool(避免两套字符串/集合工具并存)。仅当 commons-lang3 缺该能力(如 BCrypt、CollStreamUtil.groupByKey、DateUtil)才引对应 Hutool 模块,并在代码注释标注混用原因。
    • 已引 Hutool:按本指南用 Hutool,不再加 commons-lang3。
    • 判定法:读 pom.xml/build.gradle 先看已有什么 → 跟随既有库 → 仅补缺口。一句话:一个项目一套字符串/集合工具,不混用。