编程规约
命名规范
变量命名不使用拼音。
领域模型相关命名使用全大写。
UserDo
,UserDTO
常量名使用全大写,下划线分开,尽量表达清除。
MAX_STOCK_COUNT
抽象类以Abstract或者Base开头。
BaseDAO
异常类名以Exception结尾。
测试类以它要测试的类的名称开始,Test结尾。
UserServiceTest
POJO中,布尔类型变量名不要加is,某些框架会引起序列化错误。
包名统一小写、单数,类名如Utils可以复数。
接口方法不加修饰符号。
将设计模式体现在类名中
OrderFactory
,LoginProxy
,ResourceObserver
接口和实现类的命名规则:
- 对于Service和DAO,暴露接口,内部实现类使用Impl后缀。
- 对于形容能力的接口,取对应名词作为实现类。
AbstractTranslator
实现Translatable
枚举类带上Enum后缀,成员名称大写。
Service/DAO层方法命名规约。
- 获取单个对象的方法用get做前缀。
- 获取多个对象的方法法用list做前缀。
- 获取统计值方法用count做前缀。
- 插入的方法用save或insert做前缀。
- 删除的方法用remove或delete做前缀。
- 修改的方法用update做前缀。
领域模型命名规约
- 数据对象:xxxDO,xxx为表名。
- 数据传输对象:xxxDTO,xxx为业务领域相关名称。
- 展示对象:xxxVO,xxx一般为网页名称。
- POJP是DO/DTO/BO/VO的统称
常量定义
- 不允许出现魔法值(即未经定义的常量)直接出现在代码中。
- 常量应用分层
- 跨应用共享常量:放置在二方库中。
- 应用内共享常量:放置在一方库的modules的constant目录下。
- 子工程内部共享常量:在当前子工程的constant目录下。
- 包内共享常量:放在包内单独的constant目录下。
- 类内共享常量:类内部private static final。
3.如果变量值仅在一个范围内变化用Enum类。如果带有名称之外的延伸属性,必须使用Enum类
//正例:
public DayEnum{MONDAY(1),TUESDAY(2),WEDNESDAY(3),THURSDAY(4),FRIDAY(5),SATURDAY(6),SUNDAY(7);}
格式规约
- 大括号使用约定:
//正例
public static void main(String[] args) {
// 缩进 4 个空格
String say = "hello";
// 运算符的左右必须有一个空格
int flag = 0;
// 关键词 if 与括号之间必须有一个空格,括号内的 f 与左括号, 0 与右括号不需要空格
if (flag == 0) {
System.out.println(say);
}
// 左大括号前加空格且不换行;左大括号后换行
if (flag == 1) {
System.out.println("world");
// 右大括号前换行,右大括号后有 else,不用换行
} else {
System.out.println("ok");
// 在右大括号后直接结束,则必须换行
}
}
- 单行字符不超过120个,换行规则如下:
- 第二行缩进4个空格,第三行不再继续缩进。
- 运算符、方法调用符与下文一起换行。
- 逗号后换行。
//正例
dataset.add("a", "b", "c")...
.add("a", "b", "c")...
"d");
- 逗号后必须要有一个空格。
- IDE的text file encoding设置为UTF-8,换行符用Unix格式。
- 方法体内不同的业务逻辑语句组之间插入一个空格。
OOP规约
- 避免使用对象来访问静态变量或方法,直接用类名。
BeanUtils.copyProperties(model)
- 覆盖方法必须加
@Override
注解。 - 接口被调用时,不能改动,过时加
@Deprecated
注解。 - 不能使用过时的类或方法。
- 使用常量或确定有值的对象来调用
equals
,或使用Objects.equals(a,b)
方法。 - 所有包装类对象之间值的比较全部使用
equals
方法。 - 关于基本数据类型与包装数据类型的使用标准如下:
- 所有的POJO类属性必须使用包装数据类型。
Long id
,public Integer count()
- RPC方法的返回值和参数使用包装数据类型。
- 所有局部变量使用基本数据类型。
- POJO类不要设定任何属性默认值。
- 序列化类新增属性时,不要修改
serialVersionUID
。 - 构造方法禁止加入任何业务逻辑,初始化逻辑放在init方法中。
- POJO类必须写
toString
方法,注意继承类时调用super.toString()
。 - 类内定义顺序依次是:公有方法或保护方法 > 私有方法 >
getter
/setter
-
getter
/setter
方法中不要增加业务逻辑。 - 类成员与方法访问控制从严:
- 如果不允许外部通过
new
来创建对象,构造方法必须为private
。 - 工具类不允许有
public
或default
构造方法。 - 类非
static
成员变量并且与子类共享,必须是protected
。 - 类非
static
成员变量并且仅在本类使用,必须是private
。 - 类
static
成员变量仅在本类使用,必须为private
- 若是
static
成员变量,必须考虑是否为final
。 - 类成员方法仅供类内部调用,必须是
private
。 - 类成员方法只对继承类公开,那么限制为
protected
。
集合处理
- 关于
hashCode
和equals
的处理,遵循如下规则:
- 只要重写
equals
,则必须重写hashCode
。 - 如果需要用
Set
存储的对象,必须重写两个方法。 - 如果自定义对象作为
Map
的键,必须重写两个方法。
- 集合转数组使用集合的
toArray(T[] array)
,传入类型相同,大小相同的数组。
String[] strArray = new String[list.size()];
strArray = list.toArray(strArray);
- 使用工具类
Arrays.asList()
把数组转换成集合时,不能使用修改集合的方法
说明:体现了适配器模式,只是转换接口,后台的数据仍是数组。 -
Comparator
要满足如下三个条件,不然Arrays.sort
或Collections.sort
会报异常。
- x,y的比较结果和y,x的比较结果相反。
- x>y,y>z,则x>z。
- x=y,则x,z比较结果和y,z比较结果相同。
- 使用
entrySet
遍历Map
。 - 高度注意
Map
类集合能不能存储null
值的情况,如下表格:
|集合类|Key|Value|Super|说明|
|---|
|Hashtable
|不允许null
|不允许null
|Dictionary
|线程安全|
|ConcurrentHashMap
|不允许null
|不允许null
|AbstractMap
|分段锁技术|
|TreeMap
|不允许null
|允许null
|AbstractMap
|线程不安全|
|HashMap
|允许null
|允许null
|AbstractMap
|线程不安全| - 利用
Set
来去重元素。
并发处理
获取单例对象需要保证线程安全,其中的方法也要保证。
说明:资源驱动类、工具类、单例工厂都需要注意创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。
public class TimerTaskThread extends Thread {
public TimerTaskThread() {
super.setName("TimerTaskThread"); ...
}
线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
说明:使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。线程池不允许使用
Executors
去创建,而是通过ThreadPoolExecutor
的方式,以更明确线程池运行规则。SimpleDateFormat
是线程不安全的类,不要定义为static
,或使用DateUtils
工具类
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){
@Override
protected DateFormat initialValue(){
return new SimpleDateFormat("yyyy-MM-dd");
}
}
高并发时,同步调用应该去考量锁的性能损耗。无锁数据结构>锁区块>方法锁>对象锁>类锁
对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,避免死锁。
并发修改同一记录时,避免更新丢失,需要加锁。要么在应用加锁,要么在缓存加锁,要么在数据库层使用乐观锁,使用version作为更新依据。