编程军规-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。
- 2字母缩写:严禁使用驼峰写法,必须全大写。
- Boolean 校验方法
- 返回
boolean的校验方法,必须以is或has开头。 - ❌ 错误:
checkUser()(若返回布尔值) - ✅ 正确:
isUserValid() - 注:POJO 中的布尔属性变量名不要加
is(如isSuccess),否则会导致 JSON 序列化失败。
- 返回
- Map 集合命名
- 必须体现
Key到Value的映射关系,格式为{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.newFixedThreadPool、newCachedThreadPool等便捷工厂方法(底层队列或线程数无界,易 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、日期处理类规范
- 核心原则:
- 优先使用 Java 8+ 日期 API:
LocalDateTime,DateTimeFormatter(线程安全)。 - 慎用 SimpleDateFormat:
- 它是非线程安全的,禁止定义为
static成员变量。 - 如果必须使用,请在方法内部局部创建,或使用
ThreadLocal封装的工具类。
- 它是非线程安全的,禁止定义为
- DateTimeFormatter 优化:
- 定义为
static final常量,避免在循环或高频方法中反复调用ofPattern。
- 定义为
- 优先使用 Java 8+ 日期 API:
❌ 反例:
▼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。
- CPU 密集型:
- 容器限制:在 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个评论
全部评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
