|
|
@ -2,10 +2,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
## 第一章 基础编码规范
|
|
|
|
## 第一章 基础编码规范
|
|
|
|
### 1.1 命名规范
|
|
|
|
### 1.1 命名规范
|
|
|
|
- 代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。
|
|
|
|
- 代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。
|
|
|
|
tips:JDK动态代理生成的代理类 类名使用了\$符号开头,如\$Proxy1。
|
|
|
|
tips:JDK动态代理生成的代理类 类名使用了\$符号开头,如\$Proxy1。
|
|
|
|
|
|
|
|
|
|
|
|
- 代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。
|
|
|
|
- 代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。
|
|
|
|
tips:正确的英文拼写和语法可以让阅读者易于理解,避免歧义。注意,即使纯拼音命名方式也要避免采用。alibaba,yonyou,Beijing等国际通用的名称,可视同英文。
|
|
|
|
tips:正确的英文拼写和语法可以让阅读者易于理解,避免歧义。注意,即使纯拼音命名方式也要避免采用。alibaba,yonyou,Beijing等国际通用的名称,可视同英文。
|
|
|
|
在我们的财务相关模块的工程代码及数据库表设计中,可以看到一些拼音首字母缩写的命名方式,如:arap_djzb,arap是“应收应付”的英文缩写,djzb是“单据主表”的汉语拼音首字母,zdr、shr、lrr都是汉语拼音首字母缩写。当然,这些都是历史包袱,经历了这么多年的代码积累,很难从底层去修正咯,但在自己的实际编码中要以史为鉴,让自己的代码更加优雅规范,这也是同事是否尊重你的重要考量之一。
|
|
|
|
在我们的财务相关模块的工程代码及数据库表设计中,可以看到一些拼音首字母缩写的命名方式,如:arap_djzb,arap是“应收应付”的英文缩写,djzb是“单据主表”的汉语拼音首字母,zdr、shr、lrr都是汉语拼音首字母缩写。当然,这些都是历史包袱,经历了这么多年的代码积累,很难从底层去修正咯,但在自己的实际编码中要以史为鉴,让自己的代码更加优雅规范,这也是同事是否尊重你的重要考量之一。
|
|
|
|
|
|
|
|
|
|
|
@ -13,7 +13,7 @@ tips:正确的英文拼写和语法可以让阅读者易于理解,避免歧
|
|
|
|
PO / UID等。
|
|
|
|
PO / UID等。
|
|
|
|
tips:合理的类名后缀能够让我们在开发中快速地找到自己想要的代码,想看某个业务层就ctrl + shift + T搜索“XXXBO”,想看某展示层代码 就搜索“XXXVO”。
|
|
|
|
tips:合理的类名后缀能够让我们在开发中快速地找到自己想要的代码,想看某个业务层就ctrl + shift + T搜索“XXXBO”,想看某展示层代码 就搜索“XXXVO”。
|
|
|
|
|
|
|
|
|
|
|
|
- 抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类命名以它要测试的类的名称开始,以 Test 结尾。
|
|
|
|
- 抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类命名以它要测试的类的名称开始,以 Test 结尾。
|
|
|
|
例如:Spring框架的AbstractApplicationContext和Mybatis框架的BaseExecutor都是抽象类。
|
|
|
|
例如:Spring框架的AbstractApplicationContext和Mybatis框架的BaseExecutor都是抽象类。
|
|
|
|
|
|
|
|
|
|
|
|
- 方法名、参数名、成员变量、局部变量都统一使用lowerCamelCase——小驼峰风格。
|
|
|
|
- 方法名、参数名、成员变量、局部变量都统一使用lowerCamelCase——小驼峰风格。
|
|
|
@ -65,7 +65,7 @@ A) Service/DAO 层方法命名规约
|
|
|
|
3) 获取统计值的方法用 count 做前缀。
|
|
|
|
3) 获取统计值的方法用 count 做前缀。
|
|
|
|
4) 插入的方法用 save/insert 做前缀。
|
|
|
|
4) 插入的方法用 save/insert 做前缀。
|
|
|
|
5) 删除的方法用 remove/delete 做前缀。
|
|
|
|
5) 删除的方法用 remove/delete 做前缀。
|
|
|
|
6) 修改的方法用 update 做前缀。
|
|
|
|
6) 修改的方法用 update 做前缀。
|
|
|
|
B) 领域模型命名规约
|
|
|
|
B) 领域模型命名规约
|
|
|
|
1) 数据对象:xxxDO,xxx 即为数据表名。
|
|
|
|
1) 数据对象:xxxDO,xxx 即为数据表名。
|
|
|
|
2) 数据传输对象:xxxDTO,xxx 为业务领域相关的名称。
|
|
|
|
2) 数据传输对象:xxxDTO,xxx 为业务领域相关的名称。
|
|
|
@ -92,9 +92,9 @@ cache.put(key, value);
|
|
|
|
类 A 中:public static final String YES = "yes";
|
|
|
|
类 A 中:public static final String YES = "yes";
|
|
|
|
类 B 中:public static final String YES = "y";
|
|
|
|
类 B 中:public static final String YES = "y";
|
|
|
|
A.YES.equals(B.YES),预期是 true,但实际返回为 false,导致线上问题。
|
|
|
|
A.YES.equals(B.YES),预期是 true,但实际返回为 false,导致线上问题。
|
|
|
|
3)子工程内部共享常量:即在当前子工程的 constant 目录下。
|
|
|
|
3)子工程内部共享常量:即在当前子工程的 constant 目录下。
|
|
|
|
4) 包内共享常量:即在当前包下单独的 constant 目录下。
|
|
|
|
4)包内共享常量:即在当前包下单独的 constant 目录下。
|
|
|
|
5) 类内共享常量:直接在类内部 private static final 定义。
|
|
|
|
5)类内共享常量:直接在类内部 private static final 定义。
|
|
|
|
|
|
|
|
|
|
|
|
- 如果变量值仅在一个固定范围内变化用 enum 类型来定义。
|
|
|
|
- 如果变量值仅在一个固定范围内变化用 enum 类型来定义。
|
|
|
|
说明:如果存在名称之外的延伸属性应使用 enum 类型,下面正例中的数字就是延伸信息,表示一年中的第几个季节。
|
|
|
|
说明:如果存在名称之外的延伸属性应使用 enum 类型,下面正例中的数字就是延伸信息,表示一年中的第几个季节。
|
|
|
@ -119,7 +119,8 @@ public enum SeasonEnum {
|
|
|
|
- 所有的覆写方法,必须加@Override 注解。
|
|
|
|
- 所有的覆写方法,必须加@Override 注解。
|
|
|
|
说明:getObject()与 get0bject()的问题。一个是字母的 O,一个是数字的 0,加@Override 可以准确判断是否覆盖成功。另外,如果在抽象类中对方法签名(由方法名、参数的类型及**顺序** 确定唯一的方法签名)进行修改,其实现类会马上编译报错。
|
|
|
|
说明:getObject()与 get0bject()的问题。一个是字母的 O,一个是数字的 0,加@Override 可以准确判断是否覆盖成功。另外,如果在抽象类中对方法签名(由方法名、参数的类型及**顺序** 确定唯一的方法签名)进行修改,其实现类会马上编译报错。
|
|
|
|
|
|
|
|
|
|
|
|
- 相同参数类型,相同业务含义,才可以使用 Java 的可变参数,避免使用 Object。 说明:可变参数必须放置在参数列表的最后。(能用数组的就不要使用可变参数编程,可变参数在编译时会被编译成数组类型。可变参数能兼容数组类参数,但是数组类参数却无法兼容可变参数。可变参数类型必须作为参数列表的最后一项,且不能放在定长参数的前面。)
|
|
|
|
- 相同参数类型,相同业务含义,才可以使用 Java 的可变参数,避免使用 Object。
|
|
|
|
|
|
|
|
说明:可变参数必须放置在参数列表的最后。(能用数组的就不要使用可变参数编程,可变参数在编译时会被编译成数组类型。可变参数能兼容数组类参数,但是数组类参数却无法兼容可变参数。可变参数类型必须作为参数列表的最后一项,且不能放在定长参数的前面。)
|
|
|
|
正例:public List<User> listUsers(String type, Long... ids) {...}
|
|
|
|
正例:public List<User> listUsers(String type, Long... ids) {...}
|
|
|
|
|
|
|
|
|
|
|
|
- 外部正在调用或者二方库依赖的接口,不允许修改方法签名,避免对接口调用方产生影响。接口过时必须加@Deprecated 注解,并清晰地说明采用的新接口或者新服务是什么。
|
|
|
|
- 外部正在调用或者二方库依赖的接口,不允许修改方法签名,避免对接口调用方产生影响。接口过时必须加@Deprecated 注解,并清晰地说明采用的新接口或者新服务是什么。
|
|
|
@ -156,7 +157,8 @@ tips:
|
|
|
|
- **构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在 init() 方法中。**
|
|
|
|
- **构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在 init() 方法中。**
|
|
|
|
在很多client端的代码中有看到这种编码方式。
|
|
|
|
在很多client端的代码中有看到这种编码方式。
|
|
|
|
|
|
|
|
|
|
|
|
- POJO 类必须写 toString() 方法。使用 IDE 中的工具:source -> generate toString() 时,如果继承了另一个 POJO 类,注意在前面加一下 super.toString()。 说明:在方法执行抛出异常时,可以直接调用 POJO 的 toString()方法打印其属性值,便于排查问题。
|
|
|
|
- POJO 类必须写 toString() 方法。使用 IDE 中的工具:source -> generate toString() 时,如果继承了另一个 POJO 类,注意在前面加一下 super.toString()。
|
|
|
|
|
|
|
|
说明:在方法执行抛出异常时,可以直接调用 POJO 的 toString()方法打印其属性值,便于排查问题。
|
|
|
|
|
|
|
|
|
|
|
|
- 当一个类有多个构造方法,或者多个同名方法,这些方法应该按顺序放置在一起, 便于阅读。
|
|
|
|
- 当一个类有多个构造方法,或者多个同名方法,这些方法应该按顺序放置在一起, 便于阅读。
|
|
|
|
|
|
|
|
|
|
|
@ -217,7 +219,8 @@ array = list.toArray(array);
|
|
|
|
String[] strs = list.toArray(new String[0]);
|
|
|
|
String[] strs = list.toArray(new String[0]);
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
- 使用工具类 Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的add/remove/clear 方法会抛出 UnsupportedOperationException 异常。 说明:asList() 的返回对象是一个 Arrays 的内部类ArrayList(而不是java.util.ArrayList),该内部类 并没有实现集合的修改/删除等方法。Arrays.asList 体现的是适配器模式,只是转换接口,后台的数据仍是数组。
|
|
|
|
- 使用工具类 Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的add/remove/clear 方法会抛出 UnsupportedOperationException 异常。
|
|
|
|
|
|
|
|
说明:asList() 的返回对象是一个 Arrays 的内部类ArrayList(而不是java.util.ArrayList),该内部类 并没有实现集合的修改/删除等方法。Arrays.asList 体现的是适配器模式,只是转换接口,后台的数据仍是数组。
|
|
|
|
```java
|
|
|
|
```java
|
|
|
|
String[] str = new String[] { "you", "wu" };
|
|
|
|
String[] str = new String[] { "you", "wu" };
|
|
|
|
List list = Arrays.asList(str);
|
|
|
|
List list = Arrays.asList(str);
|
|
|
@ -263,7 +266,8 @@ for (String item : list) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
- 在 JDK7 版本及以上,Comparator 实现类要满足如下三个条件,不然 Arrays.sort(), Collections.sort() 会报 IllegalArgumentException 异常。 说明:三个条件如下 1) x,y 的比较结果和 y,x 的比较结果相反。 2) x>y,y>z,则 x>z。 3) x=y,则 x,z 比较结果和 y,z 比较结果相同。
|
|
|
|
- 在 JDK7 版本及以上,Comparator 实现类要满足如下三个条件,不然 Arrays.sort(), Collections.sort() 会报 IllegalArgumentException 异常。
|
|
|
|
|
|
|
|
说明:三个条件如下 1) x,y 的比较结果和 y,x 的比较结果相反。 2) x>y,y>z,则 x>z。 3) x=y,则 x,z 比较结果和 y,z 比较结果相同。
|
|
|
|
```java
|
|
|
|
```java
|
|
|
|
// 反例:下例中没有处理相等的情况,实际使用中可能会出现异常:
|
|
|
|
// 反例:下例中没有处理相等的情况,实际使用中可能会出现异常:
|
|
|
|
new Comparator<Student>() {
|
|
|
|
new Comparator<Student>() {
|
|
|
@ -444,7 +448,8 @@ public void today() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
- 除常用方法(如 getXxx/isXxx)等外,不要在条件判断中执行其它复杂的语句,将复杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性。 说明:很多 if 语句内的逻辑相当复杂,阅读者需要分析条件表达式的最终结果,才能明确什么样的条件执行什么样的语句,那么,如果阅读者分析逻辑表达式错误呢?
|
|
|
|
- 除常用方法(如 getXxx/isXxx)等外,不要在条件判断中执行其它复杂的语句,将复杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性。
|
|
|
|
|
|
|
|
说明:很多 if 语句内的逻辑相当复杂,阅读者需要分析条件表达式的最终结果,才能明确什么样的条件执行什么样的语句,那么,如果阅读者分析逻辑表达式错误呢?
|
|
|
|
```java
|
|
|
|
```java
|
|
|
|
// 正例:
|
|
|
|
// 正例:
|
|
|
|
// 伪代码如下
|
|
|
|
// 伪代码如下
|
|
|
@ -501,11 +506,11 @@ if ((file.open(fileName, "w") != null) && (...) || (...)) {
|
|
|
|
|
|
|
|
|
|
|
|
- 谨慎注释掉代码。在上方详细说明,而不是简单地注释掉。如果无用,则删除。
|
|
|
|
- 谨慎注释掉代码。在上方详细说明,而不是简单地注释掉。如果无用,则删除。
|
|
|
|
说明:代码被注释掉有两种可能性。
|
|
|
|
说明:代码被注释掉有两种可能性。
|
|
|
|
1)后续会恢复此段代码逻辑。
|
|
|
|
1)后续会恢复此段代码逻辑。
|
|
|
|
2)永久不用。前者如果没有备注信息,难以知晓注释动机。后者建议直接删掉(代码仓库保存了历史代码)。
|
|
|
|
2)永久不用。前者如果没有备注信息,难以知晓注释动机。后者建议直接删掉(代码仓库保存了历史代码)。
|
|
|
|
|
|
|
|
|
|
|
|
- 对于注释的要求:
|
|
|
|
- 对于注释的要求:
|
|
|
|
第一、能够准确反应设计思想和代码逻辑;
|
|
|
|
第一、能够准确反应设计思想和代码逻辑;
|
|
|
|
第二、能够描述业务含义,使别的程序员能够迅速了解到代码背后的信息。完全没有注释的大段代码对于阅读者形同天书,注释是给自己看的,即使隔很长时间,也能清晰理解当时的思路;注释也是给继任者看的,使其能够快速接替自己的工作。
|
|
|
|
第二、能够描述业务含义,使别的程序员能够迅速了解到代码背后的信息。完全没有注释的大段代码对于阅读者形同天书,注释是给自己看的,即使隔很长时间,也能清晰理解当时的思路;注释也是给继任者看的,使其能够快速接替自己的工作。
|
|
|
|
|
|
|
|
|
|
|
|
- 好的命名、代码结构是自解释的,注释力求精简准确、表达到位。避免出现注释的一个极端:过多过滥的注释,代码的逻辑一旦修改,修改注释是相当大的负担。
|
|
|
|
- 好的命名、代码结构是自解释的,注释力求精简准确、表达到位。避免出现注释的一个极端:过多过滥的注释,代码的逻辑一旦修改,修改注释是相当大的负担。
|
|
|
@ -535,7 +540,8 @@ put(elephant, fridge);
|
|
|
|
- 获取当前毫秒数 System.currentTimeMillis(); 而不是 new Date().getTime();
|
|
|
|
- 获取当前毫秒数 System.currentTimeMillis(); 而不是 new Date().getTime();
|
|
|
|
说明:如果想获取更加精确的纳秒级时间值,使用 System.nanoTime()的方式。在 JDK8 中, 针对统计时间等场景,推荐使用 Instant 类。
|
|
|
|
说明:如果想获取更加精确的纳秒级时间值,使用 System.nanoTime()的方式。在 JDK8 中, 针对统计时间等场景,推荐使用 Instant 类。
|
|
|
|
|
|
|
|
|
|
|
|
- 不要在视图模板中加入任何复杂的逻辑。 说明:根据 MVC 理论,视图的职责是展示,不要抢模型和控制器的活。
|
|
|
|
- 不要在视图模板中加入任何复杂的逻辑。
|
|
|
|
|
|
|
|
说明:根据 MVC 理论,视图的职责是展示,不要抢模型和控制器的活。
|
|
|
|
|
|
|
|
|
|
|
|
- 任何数据结构的构造或初始化,都应指定大小,避免数据结构无限增长吃光内存。
|
|
|
|
- 任何数据结构的构造或初始化,都应指定大小,避免数据结构无限增长吃光内存。
|
|
|
|
|
|
|
|
|
|
|
@ -550,7 +556,8 @@ put(elephant, fridge);
|
|
|
|
正例:if (obj != null) {...}
|
|
|
|
正例:if (obj != null) {...}
|
|
|
|
反例:try { obj.method(); } catch (NullPointerException e) {…}
|
|
|
|
反例:try { obj.method(); } catch (NullPointerException e) {…}
|
|
|
|
|
|
|
|
|
|
|
|
- 异常不要用来做流程控制,条件控制。 说明:异常设计的初衷是解决程序运行中的各种意外情况,且异常的处理效率比条件判断方式要低很多。
|
|
|
|
- 异常不要用来做流程控制,条件控制。
|
|
|
|
|
|
|
|
说明:异常设计的初衷是解决程序运行中的各种意外情况,且异常的处理效率比条件判断方式要低很多。
|
|
|
|
|
|
|
|
|
|
|
|
- catch 时请分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码。 对于非稳定代码的 catch 尽可能进行区分异常类型,再做对应的异常处理。
|
|
|
|
- catch 时请分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码。 对于非稳定代码的 catch 尽可能进行区分异常类型,再做对应的异常处理。
|
|
|
|
说明:对大段代码进行 try-catch,使程序无法根据不同的异常做出正确的应激反应,也不利于定位问题,这是一种不负责任的表现。
|
|
|
|
说明:对大段代码进行 try-catch,使程序无法根据不同的异常做出正确的应激反应,也不利于定位问题,这是一种不负责任的表现。
|
|
|
@ -589,7 +596,8 @@ put(elephant, fridge);
|
|
|
|
1)使用抛异常返回方式,调用方如果没有捕获到就会产生运行时错误。
|
|
|
|
1)使用抛异常返回方式,调用方如果没有捕获到就会产生运行时错误。
|
|
|
|
2)如果不加栈信息,只是 new 自定义异常,加入自己的理解的 error message,对于调用端解决问题的帮助不会太多。如果加了栈信息,在频繁调用出错的情况下,数据序列化和传输的性能损耗也是问题。
|
|
|
|
2)如果不加栈信息,只是 new 自定义异常,加入自己的理解的 error message,对于调用端解决问题的帮助不会太多。如果加了栈信息,在频繁调用出错的情况下,数据序列化和传输的性能损耗也是问题。
|
|
|
|
|
|
|
|
|
|
|
|
- 避免出现重复的代码(Don’t Repeat Yourself),即 DRY 原则。 说明:随意复制和粘贴代码,必然会导致代码的重复,在以后需要修改时,需要修改所有的副本,容易遗漏。必要时抽取共性方法,或者抽象公共类,甚至是组件化。
|
|
|
|
- 避免出现重复的代码(Don’t Repeat Yourself),即 DRY 原则。
|
|
|
|
|
|
|
|
说明:随意复制和粘贴代码,必然会导致代码的重复,在以后需要修改时,需要修改所有的副本,容易遗漏。必要时抽取共性方法,或者抽象公共类,甚至是组件化。
|
|
|
|
```java
|
|
|
|
```java
|
|
|
|
// 正例:
|
|
|
|
// 正例:
|
|
|
|
// 一个类中有多个 public 方法,都需要进行数行相同的参数校验操作,这个时候请抽取:
|
|
|
|
// 一个类中有多个 public 方法,都需要进行数行相同的参数校验操作,这个时候请抽取:
|
|
|
@ -671,7 +679,7 @@ tips:我们使用的是dr字段代表逻辑删除,且POJO和布尔字段也
|
|
|
|
- 如果存储的字符串长度几乎相等,使用 char 定长字符串类型。
|
|
|
|
- 如果存储的字符串长度几乎相等,使用 char 定长字符串类型。
|
|
|
|
tips:公司这一点倒是做的比较规范。
|
|
|
|
tips:公司这一点倒是做的比较规范。
|
|
|
|
|
|
|
|
|
|
|
|
- varchar 是不定长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长度大于此值,定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索引效率。
|
|
|
|
- varchar 是不定长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长度大于此值,定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索引效率。
|
|
|
|
tips:Oracle的varchar最大长度为4000,SQL server 8000,这是之前适配数据库时踩过的坑。
|
|
|
|
tips:Oracle的varchar最大长度为4000,SQL server 8000,这是之前适配数据库时踩过的坑。
|
|
|
|
|
|
|
|
|
|
|
|
- 表必备三字段:id, gmt_create, gmt_modified。
|
|
|
|
- 表必备三字段:id, gmt_create, gmt_modified。
|
|
|
|