程序界两大难题,缓存一致性与命名那些事儿。本文为命名规范整理;下文为命名单词速查,整理了一些常见的命名使用到的单词。
标识符:标识符由任意顺序的大小写字母(a-z,A-Z),数字(0-9)下划线(_)和美元符号($)组成,标识符常用来给类、对象、变量、方法、接口、自定义数据类型等命名的。
注意:不能把java关键字作为标识符。 从java10 开始 var 关键字可用于局部变量修饰。
用于数据类型
boolean、byte、char、 double、 float、int、long、new、short、void、instanceof
用于语句
break、case、 catch、 continue、 default 、do、 else、 for、 if、return、switch、try、 while、 finally、 throw、this、 super
用于修饰
abstract、final、native、private、 protected、public、static、synchronized、transient、 volatile
用于方法、类、接口、包和异常
class、 extends、 implements、interface、 package、import、throws
其它 future、 generic、 operator、 outer、rest、var 、goto、const、null
包名默认全部小写,单词之间以 点号隔开。
类名,默认为驼峰风格(UpperCamelCase),首字母大写
变量,默认为驼峰风格(LowerCamelCase),首字母小写
常量,默认为蛇形风格(snake_case), 字母全部大写,单字之间以下划线连接
结构
功能
一般是表示内部存放的子包、类的功能,按照功能命名即可。
如 util 包下为 xxxUtil.java 类。
抽象类命名根据好的命名规则第二点,最好使用自身意义的名称,而不是在类的名称中带有额外的标记;抽象类与接口不同,抽象类使用时是用继承的方式,类似于模板模式,其中的抽象方法也应该是对象的固有行为,属性为固有特征(可以理解为少了某一个行为都不是当前这类对象了);接口则是某一种公共的行为,是一种特征,这种行为更多特定的、扩展的行为,使用的是实现的方式,乃是辐射模式(可以理解为这个行为不影响当前对象的根本类别);java中接口可以同时实现多个而抽象类只能继承一个。
三角形的定义:三角形(triangle)是由同一平面内不在同一直线上的三条线段‘首尾’顺次连接所组成的封闭图形。
正例: Triangle(抽象类,三角形) ,Obtuse(钝角,接口) 、红色边(RedSide) -> ObtuseRedSideTriangle(实现类,钝角红边三角形)
public class ObtuseRedSideTriangle extends Triangle implements Obtuse,RedSide { ... }
综上,抽象类的命名最好直接使用其对象原有名称,不需要附加什么额外标志
异常类命名使用Exception结尾;异常类并没有特殊的关键字修复,所以其类型特征体现在了类的名称中。
public class NoSuchMethodException extends RuntimeException {}
public class AnnotationUtilsTest {}
public interface Playe { play(); }
public interface Mp3Reader { read(String filePath); }
public class Mp3Player implements Mp3Reader,Play{ xxx }
接口类中的方法和属性不要加任何修饰符号(public 也不要加)
如果是形容能力的接口名称,取对应的形容词为接口名(通常是–able 的形式) 。
对于 Service 和 DAO 类,基于 SOA 的理念,暴露出来的服务一定是接口,内部的实现类用 Impl 的后缀与接口区别。
方法名、参数名、成员变量、局部变量都统一使用 **lowerCamelCase **风格,必须遵从
驼峰形式。
常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。
代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。
反例: _name / name / n a m e / n a m e / n a m e name / name_ / name name/name/name / name
禁用拼音与英文混合的方式,更不允许直接使用中文的方式
类型与中括号紧挨相连来表示数组
正例: 定义整形数组 int[] arrayDemo;
反例: 在 main 参数中,使用 String args[]来定义。
POJO 类中布尔类型的变量,都不要加 is 前缀,否则部分框架解析会引起序列化错误。
反例: 定义为基本数据类型 Boolean isDeleted 的属性,它的方法也是 isDeleted(), RPC
框架在反向解析的时候, “误以为” 对应的属性名称是 deleted,导致属性获取不到,进而抛
出异常。
在 long 或者 Long 赋值时, 数值后使用大写的 L,不能是小写的 l,小写容易跟数字1 混淆,造成误解。
说明: Long a = 2l; 写的是数字的 21,还是 Long 型的 2
不要使用一个常量类维护所有常量, 要按常量功能进行归类,分开维护。
常量的复用层次有五层:跨应用共享常量、应用内共享常量、子工程内共享常量、包内共享常量、类内共享常量。
1) 跨应用共享常量:放置在二方库中,通常是 client.jar 中的 constant 目录下。
2) 应用内共享常量:放置在一方库中, 通常是子模块中的 constant 目录下。
反例: 易懂变量也要统一定义成应用内共享常量,两位攻城师在两个类中分别定义了表示“是”的变量:
类 A 中: public static final String YES = "yes";
类 B 中: public static final String YES = "y";
A.YES.equals(B.YES),预期是 true,但实际返回为 false,导致线上问题。
3) 子工程内部共享常量:即在当前子工程的 constant 目录下。
4) 包内共享常量:即在当前包下单独的 constant 目录下。
5) 类内共享常量:直接在类内部 private static final 定义。
示例 userList.stream().filter(it -> it.getUserName().length() > 1).collect(Collectors.toList())
方法应该是一个动词,表示一种行为,需要明确表达方法的功能;
Service/DAO 层方法命名规约
1) 获取单个对象的方法用 get 做前缀。
2) 获取多个对象的方法用 list 做前缀,复数形式结尾如: listObjects。
对于分页查询,可以使用 page 作为前缀,表示分页查询,通常同时返回当前记录总数与当前页的 。
对于搜索类模糊查询偶尔也使用 search/find作为前缀,但是考虑到其更多是一种获取对象的手段,不宜作为前缀。
3) 获取统计值的方法用 count 做前缀。
4) 插入的方法用 save/insert 做前缀。
5) 删除的方法用 remove/delete 做前缀。
6) 修改的方法用 update 做前缀。
反例:
User getUserByNameAndEmailAndPhoneAndAddressAndCreatedTime(String name,String email,String phone,String address,Date createdTime);
正例:
User getUserByPhone(String name,String phone);
User getUser(String name,String email,String phone,String address,Date createdTime);
当参数达到4个或者以上时,建议优化-封装参数到一个bean中:
User getUser(UserQueryParam userQueryParam);
public class UserQueryParam{
private String name;
private String email;
private String phone;
private String address;
private Date createdTime;
......
}
如果模块、 接口、类、方法使用了设计模式,在命名时需体现出具体模式。说明: 将设计模式体现在名字中,有利于阅读者快速理解架构设计理念。
领域模型命名规约
1) 数据对象: xxxDO, xxxEntity,xxx 即为数据表名。
2) 数据传输对象: xxxDTO, xxx 为业务领域相关的名称。
3) 展示对象: xxxVO, xxx 一般为网页名称。
4) POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO。