编程军规-Part1

答应大家的编程军规,第一部分来了,可以让CC整理到 Claude.md,这样生成的Java代码,就会遵循军规的。

一、命名规范

1、 包名(Package)命名:全小写 + 单数

核心原则:保持简洁,避免驼峰,统一单数。

  • 全小写原则:包名严禁使用驼峰命名法。
    • ❌ 错误:com.app.salesCloud
    • ✅ 正确:com.app.salescloud
  • 单数原则:包名应使用单数形式。
    • ❌ 错误:com.app.orders / com.app.utils
    • ✅ 正确:com.app.order / com.app.util
  • 层级规范:避免将多个语义层级强行拼接成一个长单词。
    • ❌ 错误:com.app.orderboq
    • ✅ 正确:com.app.order.boq

2、 类名(Class)命名:职责明确 + 后缀规范

核心原则:根据类性质决定后缀,不要产生歧义。

  • 工具类(Utils)
    • 包名用单数 util,类名必须用复数 Utils
    • ❌ 错误:OrderUtil
    • ✅ 正确:OrderUtils
  • 枚举类(Enum)
    • 统一放在 enums 包下,类名不要Enum 后缀。
    • ❌ 错误:StatusEnum
    • ✅ 正确:Status
  • 常量类(Constants)
    • 统一放在 constant 包下,类名必须Constants 结尾。
    • ❌ 错误:PortConfig
    • ✅ 正确:PortConstants
  • 接口类(Interface)
    • 严禁使用 I 前缀(那是 C# 的习惯)。
    • ❌ 错误:IUserService
    • ✅ 正确:UserService

3、 变量与方法名:拒绝缩写,语义先行

核心原则:可读性高于一切,拒绝“神秘”的命名。

  • 规范缩写
    • 2字母缩写:严禁使用驼峰写法,必须全大写。
      • ❌ 错误:OpportunityVo
      • ✅ 正确:OpportunityVO
    • 3字母及以上缩写:作为后缀时全大写,其他情况正常。
      • ✅ 正确:ApiResponse / PoPubAPI
    • 禁止随意缩写:严禁将 Condition 写成 Condi,将 Function 写成 Fu
  • Boolean 校验方法
    • 返回 boolean 的校验方法,必须以 ishas 开头。
    • ❌ 错误:checkUser() (若返回布尔值)
    • ✅ 正确:isUserValid()
    • 注:POJO 中的布尔属性变量名不要加 is(如 isSuccess),否则会导致 JSON 序列化失败。
  • Map 集合命名
    • 必须体现 KeyValue 的映射关系,格式为 {Key}To{Value}Map
    • ❌ 错误:userMap
    • ✅ 正确:userIdToUserMap

二、健壮性规范

1、集合接口返回规范:禁止返回 null

  • 核心原则
    • 如果集合为空,一律返回 Collections.emptyList() / emptyMap() / emptySet() 等不可变空集合。
    • 禁止返回 null
    • 禁止为了表示空而新建 new ArrayList() 等对象(浪费内存且语义不直观)。
    • 调用方只需使用 CollectionUtils.isEmpty() 判断,无需判 null
  • 原因
    • 避免 NullPointerException (NPE)。
    • 提高协作效率,减少繁琐的空指针检查。
    • Collections.emptyXXX() 是单例,零内存开销;new ArrayList() 会产生不必要的小对象。
  • ❌ 反例// 错误1:返回 null if (CollectionUtils.isEmpty(list)) { return null; } // 错误2:新建空对象 if (CollectionUtils.isEmpty(list)) { return new ArrayList<>(); }
  • ✅ 正例// 正确:返回不可变空列表 if (CollectionUtils.isEmpty(list)) { return Collections.emptyList(); } // 或者 DAO 层直接返回查询结果,框架通常已处理空值逻辑 return dao.findList(wrapper);
  • ⚠️ 注意
    • emptyList() 返回的是不可变集合,禁止对其进行 add/remove 操作,否则会抛出 UnsupportedOperationException
    • 如果在方法内部已经 new ArrayList() 并开始添加元素,即使最终为空,直接返回该 List 即可,无需额外判断转为 emptyList()

2、日期格式化规范

  • 核心原则
    • yyyy (小写)
    • MM (大写)
    • dd (小写)
    • HH (大写,24小时制)
    • mm (小写)
    • ss (小写)
    • 毫秒SSS (大写)
  • 常见陷阱
    • YYYY 表示“所在周的年份” (Week Year),跨周末时可能与自然年不符。
    • DD 表示“一年中的第几天”,而非“月份中的天数”。
    • hh 表示 12小时制,需配合 a (AM/PM) 使用,容易出错。 ❌ 反例
java
复制代码
// 错误:YYYY 可能导致跨年夜日期错误 DateTimeFormatter.ofPattern("YYYY-MM-dd"); // 错误:DD 表示一年中的第几天 DateTimeFormatter.ofPattern("yyyy-MM-DD"); // 错误:hh 是12小时制 DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");

✅ 正例

java
复制代码
// 正确:标准日期格式 DateTimeFormatter.ofPattern("yyyy-MM-dd"); // 正确:24小时制时间 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

3、线程创建规范:必须使用线程池

  • 核心原则
    • 严禁在应用代码中直接使用 new Thread() 创建线程。
    • 必须通过统一的线程池工厂或管理器获取线程池。
  • 原因
    • 线程创建成本高,复用线程池可提升性能。
    • 防止线程无限创建导致资源耗尽 (OOM)。 ❌ 反例
java
复制代码
public void process() { Thread t = new Thread(() -> doWork()); t.start(); // 禁止显式创建线程 }

✅ 正例

java
复制代码
@Component public class TaskService { // 注入或使用全局定义的线程池 private final ThreadPoolExecutor executor = ThreadPoolFactory.getGeneralPool(); public void process() { executor.submit(() -> doWork()); } }

4、线程池创建时机规范

  • 核心原则
    • 禁止在高频调用的业务方法内部创建线程池。
    • 线程池应在应用启动时或初始化阶段创建,作为全局/单例资源复用。
  • 原因
    • 频繁创建和销毁线程池比创建线程的危害更大,极易导致资源泄漏和性能抖动。

❌ 反例

java
复制代码
public void handleRequest(Request req) { // 错误:每次请求都创建新线程池 ExecutorService pool = Executors.newFixedThreadPool(10); pool.submit(() -> process(req)); pool.shutdown(); }

✅ 正例

java
复制代码
@Service public class RequestService { // 正确:全局复用线程池 private static final ExecutorService POOL = Executors.newFixedThreadPool(10); public void handleRequest(Request req) { POOL.submit(() -> process(req)); } }

5、线程池参数规范:必须有界且命名明确

  • 核心原则
    • 禁止使用 Executors.newFixedThreadPoolnewCachedThreadPool 等便捷工厂方法(底层队列或线程数无界,易 OOM)。
    • 必须显式指定:核心线程数、最大线程数、有界队列容量、线程名称前缀。
    • 推荐使用公司统一的线程池工厂类进行创建,以便监控和管理。 ❌ 反例
java
复制代码
// 错误:CachedThreadPool 允许创建无限线程,FixedThreadPool 队列无界 ExecutorService pool = Executors.newCachedThreadPool(); ExecutorService pool2 = Executors.newFixedThreadPool(10);

✅ 正例

java
复制代码
// 正确:显式指定所有参数,包括有界队列 ThreadPoolExecutor pool = new ThreadPoolExecutor( 10, // corePoolSize 50, // maximumPoolSize 60, // keepAliveTime TimeUnit.SECONDS, new LinkedBlockingQueue<>(1000), // 有界队列 new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略 ); // 建议设置线程名前缀以便调试

6、日期处理类规范

  • 核心原则
    1. 优先使用 Java 8+ 日期 APILocalDateTimeDateTimeFormatter (线程安全)。
    2. 慎用 SimpleDateFormat
      • 它是非线程安全的,禁止定义为 static 成员变量。
      • 如果必须使用,请在方法内部局部创建,或使用 ThreadLocal 封装的工具类。
    3. DateTimeFormatter 优化
      • 定义为 static final 常量,避免在循环或高频方法中反复调用 ofPattern

❌ 反例

java
复制代码
// 错误1:SimpleDateFormat 定义为 static,并发下必现错乱 private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); // 错误2:在方法内部反复创建 DateTimeFormatter public void parse(String dateStr) { LocalDate d = LocalDate.parse(dateStr, DateTimeFormatter.ofPattern("yyyy-MM-dd")); }

✅ 正例

java
复制代码
// 正确1:使用线程安全的 DateTimeFormatter 常量 private static final DateTimeFormatter FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd"); public void parse(String dateStr) { LocalDate d = LocalDate.parse(dateStr, FMT); } // 正确2:如果必须用 SimpleDateFormat,确保局部变量或 ThreadLocal public void parseLegacy(String dateStr) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Date d = sdf.parse(dateStr); }

7、ThreadLocal 清理规范

  • 核心原则
    • 使用完 ThreadLocal 后,必须调用 remove() 方法清理。
    • 务必使用 try-finally 块确保清理执行。
    • 特别在线程池场景中,线程复用会导致脏数据污染或内存泄漏。

❌ 反例

java
复制代码
context.set(userId); doBusiness(); // 忘记 remove,后续任务可能读到错误的 userId

✅ 正例

java
复制代码
context.set(userId); try { doBusiness(); } finally { context.remove(); // 必须清理 }
  • 💡 进阶:在线程池环境下,建议使用 TransmittableThreadLocal (如阿里 Ttl 库) 解决上下文传递问题。

8、字符串拼接规范

  • 核心原则
    • 非线程安全场景(如局部变量),必须使用 StringBuilder
    • 禁止使用 StringBuffer(除非确需线程安全,但极少见)。

❌ 反例

java
复制代码
StringBuffer sb = new StringBuffer(); // 性能较差,无需同步时使用

✅ 正例

java
复制代码
StringBuilder sb = new StringBuilder(); // 高性能,默认选择

9、线程池规模估算

  • 核心原则
    • 根据 CPU 核心数和业务类型(IO密集型/CPU密集型)合理设置线程数。
    • 经验公式
      • CPU 密集型:N + 1
      • IO 密集型:2N 或更高,但需设置有界队列防止 OOM。
    • 容器限制:在 Docker/K8s 环境中,单个服务的总线程数不宜过大(例如单实例建议不超过 100-200 个活跃线程,具体视内存而定),避免上下文切换开销过大。

10、JSON 序列化/反序列化规范

  • 核心原则
    • 禁止在业务逻辑中进行不必要的 JSON 序列化/反序列化(如为了拷贝对象而转 JSON)。
    • 仅允许在数据存储(DB/Redis/MQ)或网络传输边界使用。
    • 避免在循环或高频路径中使用反射重的 JSON 库。

❌ 反例

java
复制代码
// 错误:为了深拷贝或类型转换,先转 JSON 再转回对象,性能极差 String json = objectMapper.writeValueToString(obj); TargetObj target = objectMapper.readValue(json, TargetObj.class);

✅ 正例

java
复制代码
// 正确:使用 MapStruct 或 BeanUtils
0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
王保保
下载 APP