
接手一个运营了三年的交易系统我翻到 service 层第一个实现类映入眼帘的是一串变量String a ;、Double b 0d;往下再翻还有zhekou、userJF、bSave这种拼音和英文混着来的命名。那天我最大的感慨是这个项目真正的技术债不在架构不在中间件而是连名字都没起明白。后来我们花了一周时间集中清理命名改动不大但代码可读性直接上了一个档次。这也是我为什么始终推荐团队把阿里巴巴《Java开发手册》里的“命名风格”当成第一优先级来执行。命名风格篇是整个手册“编程规约”的开篇内容不多但每一条背后都是真实生产环境的坑。这篇就结合我自己的工作经历把命名风格篇里容易忽视的细节、踩过的坑以及怎么在团队里落地完整梳理一遍。适合刚入职的新人也适合正在带团队的朋友。1. 命名风格排在编程规约第一位不是没道理的1.1 名字难看代码再漂亮也白搭国内很多团队一谈代码规范第一反应就是“缩进几个空格、行宽多少、要不要空行”。这些当然重要但格式问题 IDE 一个快捷键就能收拾干净真正让维护者崩溃的永远是命名。我接手过不少“历史项目”印象最深的是某个交易模块变量名叫a、b、str方法名叫doThing、handleIt还有个枚举常量叫STATUS_1。这种代码你别说 review连定位一个 bug 都要把整个类从头读一遍效率低到怀疑人生。阿里《Java开发手册》把“命名风格”放在编程规约的第一节本质上是在强调一个事代码首先是写给人看的其次才是给机器执行的。命名是代码可读性的第一接触面一个变量从声明到使用名字会在几十行代码里反复出现名字起得不到位后面所有注释、文档、设计都在帮倒忙。1.2 自解释是最高目标不是“英文好”的要求规约里有一条推荐项我特别认同“任何自定义编程元素在命名时使用尽量完整的单词组合来表达其意。”说白了就是见名知义。比如方法叫queryOrderListByUserId你不用看实现就知道它干嘛如果叫qryOrder你还得点进去确认是查单个还是查列表、参数是userId还是orderId。经常有人说“我英文不好所以名字起得短”。其实命名难处不在词汇量而在你愿不愿意把意图完整表达出来。现在 IDE 自动补全这么强长名字的输入成本早就被抵消了真正贵的是读代码时猜名字的成本。我在评审时经常举一个例子list和listOrdersByUserIdAndStatus前者要靠上下文猜后者自带完整语义这就是“自解释”的差别。1.3 强制项、推荐项、参考项执行优先级怎么排手册里把规则分成三个等级强制、推荐、参考。我的建议是团队落地时先把所有强制项做成自动化检查比如接入 IDE 插件或 CI 规则推荐项靠 Code Review 把握参考项更多是场景化建议比如分层命名规范定团队规范时可以直接引用。这样分层的意义在于机器能查的不要浪费人肉人只需要盯机器查不出来的东西。命名规约里的强制项大多很机械比如“是否驼峰”“是否全大写”交给插件扫一遍基本能清掉八成问题剩下的“这个名字是否表意准确”“这个缩写是否会让别人误解”才需要真正有经验的工程师去判断。2. 类名与接口名帕斯卡命名之外的几条强制红线2.1 类名 UpperCamelCase以及那串“例外缩写”类名使用大驼峰UpperCamelCase比如OrderController、UserService。但规约里有个容易忽略的例外DO、BO、DTO、VO、AO、PO、UID这类约定俗成的缩写在类名里允许连续大写。所以正确的是UserDTO、OrderVO而不是UserDto、OrderVo。这个例外很务实。这些缩写已经是 Java 生态的通用后缀你硬要写成Dto反而显得不专业。我见过一个项目里同时存在UserDto、UserVO、UserDo三种写法每次看到都要确认是不是同一个东西。团队如果出现这种情况建议直接按规约统一成UserDTO、UserVO、UserDO一次替换到位。2.2 抽象类、异常类、测试类的命名是硬约束规约明确要求抽象类命名用Abstract或Base开头异常类命名用Exception结尾测试类命名以它要测试的类的名称开始以Test结尾。这几个前后缀不是风格偏好而是让别人在阅读代码的一瞬间就能识别类型。比如看到BaseEntity你立刻知道不能直接 new看到PaymentException你立刻知道它是异常而不是普通类看到UserServiceTest你立刻知道这是测试入口。命名约定本质上是一种“视觉压缩”把类型信息直接编码进名字里读代码时就不用反复跳转确认。2.3 枚举类的名字和成员大小写要分开控枚举类名建议带Enum后缀比如ProcessStatusEnum、PayChannelEnum。而枚举成员名称必须全大写、单词间用下划线隔开比如WAIT_PAYMENT、FINISHED。这里很多新手会踩坑成员名写成WaitPayment或waitPayment。其实枚举成员本质上是常量走的就是常量命名规则全大写加下划线是最基本的要求。还有一种情况是枚举类名不统一有的叫OrderStatusEnum、有的叫OrderStatus扫描代码时很难通过名字快速过滤出所有枚举。建议在团队规范里直接把Enum后缀定为强制项省得后面做代码分析时还得靠人工去认。2.4 接口命名I 前缀不是必须的别自嗨在 Java 里接口命名有个常见误区受 C# 影响有人喜欢给接口加I前缀比如IUserService、IOrderDAO。阿里规约没有把I前缀作为强制项反而推荐了两套更符合 Java 生态的做法。第一套是面向 Service 和 DAO 这一类业务接口暴露出来的服务定义成接口内部实现类用Impl后缀区分比如UserService接口 UserServiceImpl实现类。第二套是能力型接口用形容词命名最常见的就是 JDK 里的Serializable、Runnable、Comparable它们表示“这个类具备什么能力”。另外还需要注意接口里定义的方法和属性不要加public修饰符因为接口成员本来就默认是 public写了反而显得啰嗦。这不是命名风格但和接口的可读性直接相关顺手说说。3. 方法、变量与常量的命名最容易翻车的三个细节3.1 方法命名动词前缀必须各司其职规约对 Service 和 DAO 层的方法命名有一套推荐前缀获取单个对象用get获取多个对象用list复数结尾统计用count插入用save/insert删除用remove/delete修改用update。前缀返回语义示例getXxx获取单个对象getUserById(Long id)listXxx获取多个对象复数结尾listUsersByStatus(String status)countXxx统计数量countUsersByStatus(String status)saveXxx / insertXxx插入saveOrder(OrderDO order)updateXxx更新updateOrder(OrderDO order)removeXxx / deleteXxx删除deleteOrderById(Long id)这套前缀看着简单难在坚持。很多项目写着写着就变成queryUser、findScore、getAllData混着来。混用的代价是什么调用方没法通过方法名推测方法行为必须点进实现看代码协作成本直线上升。建议团队把上面这张前缀表直接贴进开发规范文档里命名时按表查不要自由发挥。3.2 布尔变量与 is 前缀一个会引发线上问题的“小细节”这一条我必须重点讲因为它带来的线上问题我处理过不止一次。规约原文是POJO类中布尔类型的变量都不要加is前缀否则部分框架解析会引起序列化错误。原理其实不复杂。JavaBean 规范对boolean类型的 getter 方法名有特殊约定基本类型boolean的 getter 通常叫isXxx()包装类型Boolean的 getter 通常叫getXxx()。假设你写了private Boolean isSuccess然后用 Lombok 的Data生成 getterLombok 对包装类型Boolean通常生成的是getSuccess()而不是getIsSuccess()。等 Jackson 序列化时字段名就变成了success而不是你期望的isSuccess。结果就是前端拿到的是{success: true}后端代码里写的是isSuccess两边的字段对不上。排查这个问题通常要花掉一下午起因却只是一个多余的is前缀。所以规约才把它列为强制项。如果你在现有代码里看到private Boolean isSuccess这种写法建议打开序列化后的 JSON 看一眼字段大概率已经不是isSuccess了。业务方法里同理不要用isXxx来表示“是否”之外的业务动作。is前缀在语义上已经被“是否判断”占用了比如isDeleted()表示“是否已删除”如果用来做“删除操作”就不伦不类。3.3 常量命名全大写是底线“怕名字太长”是误区常量命名要求全大写、单词间下划线分隔比如MAX_STOCK_COUNT、DEFAULT_PAGE_SIZE。这里最常见的两种不当写法一种是写MAX、DEF、CNT这种缩写另一种是用驼峰写maxStockCount。缩写的危害在于每个缩写都需要在上下文里二次解码解码失败就是 bug。有人担心常量名太长影响阅读其实常量一般定义在类的顶部或专门的 Constant 类里调用处通过 IDE 自动补全名字长一点也不影响效率。另外还要提醒一句常量尽量少放到接口里。规约说“尽量不要在接口里定义常量”因为接口常量会被实现类继承容易引起命名冲突和耦合。我见过一个项目把几十个常量都塞进一个 ConstantInterface实现类implements它之后代码里飘着一堆莫名其妙的“上级赠送”常量。正确的做法是放到专门的final class里或者放到对应业务类中。3.4 拼音、缩写、易混字符看着基础破坏力最大规约明令杜绝完全不规范的缩写避免望文不知义。同时不允许拼音和英文混合使用。比如userJF、zhekou、bSave这类命名在我评审过的老项目里屡见不鲜。为什么这么严因为拼音在 IDE 搜索、全局替换、跨团队协作里都会造成额外负担。你写个userJF可能只有你自己知道JF是“积分”你写个dk没人能猜出这是“折扣”还是“端口”。这里有一个例外可以记住文化专有名词允许用拼音比如DangDang但正常业务词汇必须用英文。还有一个基础到容易被忽视的避免0和o、1和l互相混淆。以前我见过一个变量名叫l1字母 l 加数字 1在那个上下文里简直可以直接当加密题。这种名字一旦出现在日志或报错信息里排查时能把人逼疯。另外变量名不能以下划线或美元符号开头、结尾这也是强制项主要是为了避免和某些框架生成的代码命名冲突。4. 包名与分层命名团队协作里的“通用语言”4.1 反域名包名与单复数一条容易“破功”的规则包名要求统一小写用反域名开头比如com.company.project.module。点分隔符之间有且仅有一个自然语义的英语单词不能有下划线也不能用驼峰。规约里还有一条很细的规则包名统一使用单数形式但是类名如果有复数含义类名可以使用复数形式。这个细节听起来绕实际场景里很常见。比如包名叫com.company.user而不是users但类名可能是UserListResponse这种带复数含义的类。这样做的目的就是让包名保持整洁类名负责表达数量含义。4.2 分层后缀怎么配Controller、Service、DAO、DO、DTO、VO一个团队最怕的不是没有规范而是有多套规范同时在跑。阿里规约的参考部分给了很清晰的分层命名建议我这里按自己的落地经验再展开一下。Controller 层XxxController负责接收请求和返回响应不写业务逻辑。Service 层XxxService接口 XxxServiceImpl实现类承载核心业务逻辑。数据访问层MyBatis 场景下统一叫XxxMapper非 MyBatis 场景可以叫XxxDAO但一个项目只能选一种不能这边UserMapper那边OrderDAO混着来。数据对象数据库表映射对象叫XxxDO如UserDO数据传输对象叫XxxDTO展示对象叫XxxVO。我曾见过一个项目里同一个人昨天写UserDAO今天写UserRepository后天又写UserMapper。三个名字指向同一个数据访问对象新人进来根本分不清该用哪个。这种问题不是技术问题是团队没有把“通用语言”定下来。4.3 命名失真的本质分层塌了命名风格其实能反映架构健康度。比如 Service 层的方法直接返回 Controller 层的 VO或者 Controller 直接把 DO 序列化后返回前端——这些都是命名与分层不匹配的典型信号。规约里区分DO