代码格式
-
【强制】如果大括号内为空,简洁地写成{},大括号中间无需换行和空格;如果是非空代码块,则:
(1)左大括号前不换行。 (2)左大括号后换行。 (3)右大括号前换行。
(4)右大括号后还有else
等代码则不换行;表示终止的右大括号后必须换行。 -
【强制】左小括号和右边相邻字符之间不需要空格;右小括号和左边相邻字符之间也不需要空格;而左大括号前需要加空格。
反例::if (空格a == b空格)
-
【强制】
if/for/while/switch/do
等保留字与左右括号之间都必须加空格。 -
【强制】任何二目、三目运算符的左右两边都需要加一个空格。 说明:包括赋值运算符
=
、逻辑运算符&&
、加减乘除符号等。 -
【强制】采用4个空格缩进,禁止使用Tab字符。 说明:如使用Tab缩进,必须设置1个Tab为4个空格。IDEA设置Tab为4个空格时,请勿勾选Use tab character。 正例:(涉及上述中的1-5点)
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"); //在右大括号后直接结束,则必须换行广 } } ```
-
【强制】注释的双斜线与注释内容之间有且仅有一个空格。 正例:
String("demo"); ```
-
【强制】在进行类型强制转换时,右括号与强制转换值之间不需要任何空格隔开。 正例:
java double first = 3.2D; int second = (int)first+2;
-
【强制】单行字符数限制不超过120个,超出需要换行,换行时遵循如下原则:
(1)第二行相对第一行缩进4个空格,从第三行开始,不再继续缩进,参考示例。
(2)运算符与下文一起换行。
(3)方法调用的点符号与下文一起换行。
(4)方法调用中的多个参数需要换行时,在逗号后进行。
(5)在括号前不要换行。
正例://超过120个字符的情况下,换行缩进4个空格,并且方法前的点号一起换行 builder.append("yang").append("hao")... .append("chen")... .append("chen")... .append("chen"); ``` <font color =A52A2A>反例:</font> ```java StringBuilder builder = new StringBuilder();//超过120个字符的情况下,不要在括号前换行 builder append("you").append("are")...append ("lucky"); //参数很多的方法调用可能超过120个字符,逗号后才是换行处 method(args1,args2,args3,… ,argsX);
-
【强制】方法参数在定义和传入时,多个参数逗号后面必须加空格。 正例:下例中实参的
args1
逗号后边必须要有一个空格。
```java method(args1, args2, args3); ```
- 【强制】IDE的text file encoding设置为UTF-8;IDE中文件的换行符使用Unix格式,不要使用Windows格式。
- 【推荐】不同逻辑、不同语义、不同业务的代码之间插入一个空行,分隔开来以提升可读性。 说明:任何情形,没有必要插入多个空行进行隔开。
OOP规约
-
【强制】避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可。
-
【强制】所有的覆写方法,必须加
@Override
注解。 说明:getObjec()
与getObject()
的问题。一个是字母的O
,一个是数字的0
,加@Override
可以准确判断是否覆盖成功。另外,如果在抽象类中对方法签名进行修改,其实现类会马上编译报错。 -
【强制】外部正在调用的接口或者二方库依赖的接口,不允许修改方法签名,避免对接口调用方产生影响。接口过时必须加@Deprecated注解,并清晰地说明采用的新接口或者新服务是什么。
-
【强制】
Object
的equals
方法容易抛空指针异常,应使用常量或确定有值的对象来调用equals
。
正例:"test".equals(param);
Cparam.equals("test");
说明:推荐使用JDK7引入的工具类java.util.Objects#equals(Object a,Object b)
** -
【强制】所有整型包装类对象之间值的比较,全部使用equals方法比较。
说明:对于Integer var=?
在-128
至127
之间的赋值,Integer
对象是在IntegerCache.cache
产生,会复用已有对象,这个区间内的Integer
值可以直接使用==
进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用
equals
方法进行判断。 -
【强制】任何货币金额,均以最小货币单位且为整型类型进行存储。
-
【强制】浮点数之间的等值判断,基本数据类型不能使用
==
进行比较,包装数据类型不能使用equals
进行判断。
说明:浮点数采用“尾数+阶码”的编码方式,类似于科学计数法的“有效数字+指数”的表示方式。二进制无法精确表示大部分的十进制小数,具体原理参考《码出高效》。反例://预期进入此代码块,执行其它业务逻辑 //但事实上a==b的结果为false } Float x = Float.valueOf(a); Float y = Float.valueOf(b); if (x.equals(y) { //预期进入此代码块,执行其它业务逻辑 //但事实上equals的结果为false } ```<font color =#228B22>正例:</font> (1)指定一个误差范围,两个浮点数的差值在此范围之内,则认为是相等的。 ```java float a = 1.0F-0.9F; float b = 0.9F-0.8F; float diff = 1e-6F; if (Math.abs(a - b) < diff) { System.out.println("true"); }
(2)使用 Big Decimal来定义值,再进行浮点数的运算操作。
BigDecimal("0.9"); BigDecimal c = new BigDecimal("0.8"); BigDecimal x = a.subtract(b); BigDecimal y = b.subtract(c); if (x.compareTo(y) == 0) { System out.println("true"); } ```
-
【强制】BigDecimal的等值比较应使用
compareTo()
方法,而不是equals()
方法。
说明:equals()
方法会比较值和精度(1.0
与1.00
返回结果为false
),而compareTo()
则会忽略精度。 -
【强制】定义数据对象
DO
类时,属性类型要与数据库字段类型相匹配。
正例:数据库字段的bigint
必须与类属性的Long
类型相对应。
反例:某业务的数据库表id
字段定义类型为bigint unsigned
,实际类对象属性为
Integer
,随着id
越来越大,超过Integer
的表示范围而溢出成为负数,此时数据库id
不支持存入负数抛出异常产生线上故障。 -
【强制】禁止使用构造方法
BigDecimal(double)
的方式把double
值转化为BigDecimal
对象
说明:BigDecimal(double)
存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常。如:BigDecimal g = new Big Decimal(0.1F)
;实际的存储值为:0.100000001490116119384765625
正例:优先推荐入参为String
的构造方法,或使用
BigDecimal
的valueOf
方法,此方法内部其实执行了
Double
的toString
,而Double
的toString
按
double
的实际能表达的精度对尾数进行了截断。recommend2 = BigDecimal.valueOf(0.1); ```
-
关于基本数据类型与包装数据类型的使用标准如下:
(1)【强制】所有的POJO
类属性必须使用包装数据类型。
(2)【强制】RPC
方法的返回值和参数必须使用包装数据类型。
(3)【推荐】所有的局部变量使用基本数据类型。
说明:POJO
类属性没有初值是提醒使用者在需要使用时,必须自己显式地进行赋值,任何NPE
问题,或者入库检查,都由使用者来保证。
正例:数据库的查询结果可能是null
,因为自动拆箱,用基本数据类型接收有NPE
风险。
反例:某业务的交易报表上显示成交总额张跌情况,即正负×%
,×
为基本数据类型,调用的RPC
服务,调用不成功时,返回的是默认值,页面显示为0%
,这是不合理的,应该显示成中划线-
。所以包装数据类型的
null
值,能够表示额外的信息,如:远程调用失败,异常退出。 -
【强制】定义
DO/PO/DTO/VO
等POJO
类时,不要设定任何属性默认值。 反例:某业务的DO
的createTime
默认值为new Date()
;但是这个属性在数据提取时并没有置入具体值,在更新其它字段时又附带更新了此字段,导致创建时间被修改成当前时间。 -
【强制】序列化类新增属性时,请不要修改
serialVersionUID
字段,避免反序列失败;如果完全不兼容升级,避免反序列化混乱,那么请修改
serialVersionUID
值。
说明:注意serialVersionUID
不一致会抛出序列化运行时异常。 -
【强制】构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在init方法中。
-
【强制】
POJO
类必须写toString
方法。使用IDE中的工具source>generate toString
时,如果继 承了另一个POJO
类,注意在前面加一下super.toString()
。
说明:在方法执行抛出异常时,可以直接调用POJO
的toString()
方法打印其属性值,便于排查问题。 -
【强制】禁止在
POJO
类中,同时存在对应属性xxx
的isXxx()
和getXxx()
方法。
说明:框架在调用属性×××
的提取方法时,并不能确定哪个方法一定是被优先调用到,神坑之一。 -
【推荐】使用索引访问用
String
的split
方法得到的数组时,需做最后一个分隔符后有无内容的检查,否则会有抛IndexOutOfBoundsException
的风险。
说明://预期大于3,结果等于3 System.out.println(ary.length); ```
-
【推荐】当一个类有多个构造方法,或者多个同名方法,这些方法应该按顺序放置在一起,便于阅读,此条规则优先于下一条。
正例:param 1, int param2); private void method(); ```
-
【推荐】类内方法定义的顺序依次是:公有方法或保护方法>私有方法>getter/setter方法。
说明:公有方法是类的调用者和维护者最关心的方法,首屏展示最好;保护方法虽然只是子类关心,也可能是"模板设计模式”下的核心方法;而私有方法外部一般不需要特别关心,是一个黑盒实现;因为承载的信息价值较低,所有Service
和DAO
的getter/setter
方法放在类体最后。 -
【推荐】
setter
方法中,参数名称与类成员变量名称一致,this.成员名 = 参数名
。在getter/setter
方法中,不要增加业务逻辑,增加排查问题的难度。 反例:this.data + 100; } else { return this.data - 100; ```
-
【推荐】循环体内,字符串的连接方式,使用
StringBuilder
的append
方法进行扩展。 反例:str + "hello"; } ``` <font color =#CD853F> 说明:</font>反编译出的字节码文件显示每次循环都会`new` 出一个`StringBuilder`对象,然后进行`append`操作,最后通过`toString()`返回`String`对象,造成内存资源浪费。
-
【推荐】
final
可以声明类、成员变量、方法、以及本地变量,下列情况使用final
关键字: (1)不允许被继承的类,如:String
类。 (2)不允许修改引用的域对象,如:POJO
类的域变量。
(3)不允许被覆写的方法,如:POJO
类的setter
方法。 (4)不允许运行过程中重新赋值的局部变量。
(5)避免上下文重复使用一个变量,使用final
关键字可以强制重新定义一个变量,方便更好地进行重构。 -
【推荐】慎用
Object
的clone
方法来拷贝对象。
说明:对象clone
方法默认是浅拷贝,若想实现深拷贝需覆写clone
方法实现域对象的深度遍历式拷贝。