编程规约

1.命名风格

  • 【强制】所有编程相关的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。

反例:DaZhePromotion [打折] / getPingfenByName() [评分] / int 某变量 = 3

  • 【强制】类名使用 UpperCamelCase 风格,但以下情形例外:DO / BO / DTO / VO / AO / PO / UID 等。

正例:ForceCode / UserDO / HtmlDTO / XmlService / TcpUdpDeal / TaPromotion
反例:forcecode / UserDo / HTMLDto / XMLService / TCPUDPDeal / TAPromotion

  • 【强制】类型与中括号挨近相连来表示数组

正例:定义整形数组 int[] arrayDemo;
反例:在 main 参数中,使用 String args[]来定义。

  • 【强制】避免在子父类的成员变量之间、或者不同代码块的局部变量之间采用完全相同的命名,

使可读性降低。

  1. public class ConfusingName {
  2. public int stock;
  3. // 非 setter/getter 的参数名称,不允许与本类成员变量同名
  4. public void get(String alibaba) {
  5. if (condition) {
  6. final int money = 666;
  7. // ...
  8. }
  9. for (int i = 0; i < 10; i++) {
  10. // 在同一方法体中,不允许与其它代码块中的 money 命名相同
  11. final int money = 15978;
  12. // ...
  13. }
  14. }
  15. }
  16. class Son extends ConfusingName {
  17. // 不允许与父类的成员变量名称相同
  18. public int stock;
  19. }
  • 【强制】杜绝完全不规范的缩写,避免望文不知义。

反例:AbstractClass“缩写”命名成 AbsClass;condition“缩写”命名成 condi,此类随意缩写严重降
低了代码的可阅读性。

  • 【推荐】为了达到代码自解释的目标,任何自定义变成元素在命名时,使用尽量完整的单词组合来表达。

正例:在 JDK 中,对某个对象引用的 volatile 字段进行原子更新的类名为:AtomicReferenceFieldUpdater。
反例:常见的方法内变量为 int a;的定义方式。

  • 【推荐】如果模块、接口、类、方法使用了设计模式,在命名时需要体现出具体模式。

说明:将设计模式体现在名字中,有利于阅读者快速理解架构设计理念。
正例: public class OrderFactory;
public class LoginProxy;
public class ResourceObserver;

  • 【推荐】接口类中的方法和属性不要加任何修饰符号(public 也不加),保持代码的简洁性,并加上有效的Javadoc注释。尽量不要在接口里定义变量,如果一定要定义变量,确定与接口方法相关,并且是整个应用的基础常量。

正例:接口方法签名 void commit();
接口基础常量 String COMPANY = “alibaba”;
反例:接口方法定义 public abstract void f();
说明:JDK8 中接口允许有默认实现,那么这个 default 方法,是对所有实现类都有价值的默认实现。

  • 接口和实现类的命名有两套规则:

    1)【强制】对于service和DAO类,基于SOA的理念,暴露出来的服务一定是接口,内部的实现类用lmpl的后缀与接口区别。
    正例:CacheServiceImpl 实现 CacheService 接口。
    2)【推荐】如果是形容能力的接口名称,取对应的形容词为接口名(通常是–able 的形容词)。
    正例:AbstractTranslator 实现 Translatable 接口。

  • 【参考】各层命名规约:

A) Service/DAO 层方法命名规约
1) 获取单个对象的方法用 get 做前缀。
2) 获取多个对象的方法用 list 做前缀,复数结尾,如:listObjects。
3) 获取统计值的方法用 count 做前缀。
4) 插入的方法用 save/insert 做前缀。
5) 删除的方法用 remove/delete 做前缀。
6) 修改的方法用 update 做前缀。
B) 领域模型命名规约
1) 数据对象:xxxDO,xxx 即为数据表名。
2) 数据传输对象:xxxDTO,xxx 为业务领域相关的名称。
3) 展示对象:xxxVO,xxx 一般为网页名称。
4) POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO。

2.常量定义

  • 【强制】不允许任何魔法值(即未经预先定义的常量)直接出现在代码中。

    1. //本例中同学 A 定义了缓存的 key,然后缓存提取的同学 B 使用了 Id#taobao 来提取,少了下划线,导致故障。
    2. String key = "Id#taobao_" + tradeId;
    3. cache.put(key, value);
  • 【推荐】不要使用一个常量类维护所有常量,要按常量功能进行归类,分开维护。

说明:大而全的常量类,杂乱无章,使用查找功能才能定位到修改的常量,不利于理解,也不利于维护。
正例:缓存相关常量放在类 CacheConsts 下;系统配置相关常量放在类 ConfigConsts 下。

  • 【推荐】常量的复用层次有五层:跨应用共享常量、应用内共享常量、子工程内共享常量、包内共享常量、类内共享常量。

1) 跨应用共享常量:放置在二方库中,通常是 client.jar 中的 constant 目录下。
2) 应用内共享常量:放置在一方库中,通常是子模块中的 constant 目录下。
反例:易懂变量也要统一定义成应用内共享常量,两位工程师在两个类中分别定义了“YES”的变量:
类 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 定义

  • 【推荐】如果变量值仅在一个固定范围内变化用enum类型来定义

说明:如果存在名称之外的延伸属性应使用 enum 类型,下面正例中的数字就是延伸信息,表示一年中的
第几个季节。

  1. public enum SeasonEnum {
  2. SPRING(1), SUMMER(2), AUTUMN(3), WINTER(4);
  3. private int seq;
  4. SeasonEnum(int seq) {
  5. this.seq = seq;
  6. }
  7. public int getSeq() {
  8. return seq;
  9. }
  10. }

3.代码格式

  • 【强制】if/for/while/switch/do 等保留字与括号之间都必须加空格。

  • 【强制】任何二目、三目运算符的左右两边都需要加一个空格。

说明:包括赋值运算符=、逻辑运算符&&、加减乘除符号等。

  • 【强制】采用 4 个空格缩进,禁止使用 tab 字符。

说明:如果使用 tab 缩进,必须设置 1 个 tab 为 4 个空格。IDEA 设置 tab 为 4 个空格时,请勿勾选 Use tab character;而在 eclipse 中,必须勾选 insert spaces for tabs。

  • 【强制】注释的双斜线与注释内容之间有且仅有一个空格。

  • 【强制】单行字符数限制不超过 120 个,超出需要换行,换行时遵循如下原则:

1)第二行相对第一行缩进 4 个空格,从第三行开始,不再继续缩进,参考示例。
2)运算符与下文一起换行。
3)方法调用的点符号与下文一起换行。
4)方法调用中的多个参数需要换行时,在逗号后进行。
5)在括号前不要换行,见反例

正例

  1. StringBuilder sb = new StringBuilder();
  2. // 超过 120 个字符的情况下,换行缩进 4 个空格,并且方法前的点号一起换行
  3. sb.append("zi").append("xin")...
  4. .append("huang")...
  5. .append("huang")...
  6. .append("huang");

反例

  1. StringBuilder sb = new StringBuilder();
  2. // 超过 120 个字符的情况下,不要在括号前换行
  3. sb.append("you").append("are")...append
  4. ("lucky");
  5. // 参数很多的方法调用可能超过 120 个字符,逗号后才是换行处
  6. method(args1, args2, args3, ...
  7. , argsX);
  • 【强制】方法参数在定义和传入时,多个参数逗号后边必须加空格。

  • 【推荐】单个方法的总行数不超过 80 行。

说明:除注释之外的方法签名、左右大括号、方法内代码、空行、回车及任何不可见字符的总行数不超过80 行。
正例:代码逻辑分清红花和绿叶,个性和共性,绿叶逻辑单独出来成为额外方法,使主干代码更加清晰;共性逻辑抽取成为共性方法,便于复用和维护

4.OOP规约

  • 【强制】避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可。
  • 【强制】外部正在调用或者二方库依赖的接口,不允许修改方法签名,避免对接口调用方产生影响。接口过时必须加@Deprecated 注解,并清晰地说明采用的新接口或者新服务是什么。

  • 【强制】Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用 equals。

正例:”test”.equals(object);
反例:object.equals(“test”);
说明:推荐使用 java.util.Objects#equals(JDK7 引入的工具类)。

  • 【强制】浮点数之间的等值判断,基本数据类型不能用==来比较,包装数据类型不能用 equals来判断

说明:浮点数采用“尾数+阶码”的编码方式,类似于科学计数法的“有效数字+指数”的表示方式。二进制无法精确表示大部分的十进制小数
反例:

  1. float a = 1.0f - 0.9f;
  2. float b = 0.9f - 0.8f;
  3. if (a == b) {
  4. // 预期进入此代码快,执行其它业务逻辑
  5. // 但事实上 a==b 的结果为 false
  6. }
  7. Float x = Float.valueOf(a);
  8. Float y = Float.valueOf(b);
  9. if (x.equals(y)) {
  10. // 预期进入此代码快,执行其它业务逻辑
  11. // 但事实上 equals 的结果为 false
  12. }

正例:

  1. (1) 指定一个误差范围,两个浮点数的差值在此范围之内,则认为是相等的。
  2. float a = 1.0f - 0.9f;
  3. float b = 0.9f - 0.8f;
  4. float diff = 1e-6f;
  5. if (Math.abs(a - b) < diff) {
  6. System.out.println("true");
  7. }
  8. (2) 使用 BigDecimal 来定义值,再进行浮点数的运算操作。
  9. BigDecimal a = new BigDecimal("1.0");
  10. BigDecimal b = new BigDecimal("0.9");
  11. BigDecimal c = new BigDecimal("0.8");
  12. BigDecimal x = a.subtract(b);
  13. BigDecimal y = b.subtract(c);
  14. if (x.equals(y)) {
  15. System.out.println("true");
  16. }
  • 【强制】禁止使用构造方法 BigDecimal(double)的方式把 double 值转化为 BigDecimal 对象。

说明:BigDecimal(double)存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常。Java 开发手册
如:BigDecimal g = new BigDecimal(0.1f); 实际的存储值为:0.10000000149
正例:优先推荐入参为 String 的构造方法,或使用 BigDecimal 的 valueOf 方法,此方法内部其实执行了
Double 的 toString,而 Double 的 toString 按 double 的实际能表达的精度对尾数进行了截断。
BigDecimal recommend1 = new BigDecimal(“0.1”);
BigDecimal recommend2 = BigDecimal.valueOf(0.1);

  • 12.关于基本数据类型与包装数据类型的使用标准如下:

1) 【强制】所有的 POJO 类属性必须使用包装数据类型。
2) 【强制】RPC 方法的返回值和参数必须使用包装数据类型。
3) 【推荐】所有的局部变量使用基本数据类型。
说明:POJO 类属性没有初值是提醒使用者在需要使用时,必须自己显式地进行赋值,任何 NPE 问题,或
者入库检查,都由使用者来保证。
正例:数据库的查询结果可能是 null,因为自动拆箱,用基本数据类型接收有 NPE 风险。
反例:某业务的交易报表上显示成交总额涨跌情况,即正负 x%,x 为基本数据类型,调用的 RPC 服务,调
用不成功时,返回的是默认值,页面显示为 0%,这是不合理的,应该显示成中划线-。所以包装数据类型
的 null 值,能够表示额外的信息,如:远程调用失败,异常退出。

  • 【强制】POJO 类必须写 toString 方法。使用 IDE 中的工具:source> generate toString时,如果继承了另一个 POJO 类,注意在前面加一下 super.toString。

说明:在方法执行抛出异常时,可以直接调用 POJO 的 toString()方法打印其属性值,便于排查问题。

  • 【推荐】 类内方法定义的顺序依次是:公有方法或保护方法 > 私有方法 > getter / setter

方法。

  • 【推荐】循环体内,字符串的连接方式,使用 StringBuilder 的 append 方法进行扩展。

说明:下例中,反编译出的字节码文件显示每次循环都会 new 出一个 StringBuilder 对象,然后进行 append
操作,最后通过 toString 方法返回 String 对象,造成内存资源浪费。
反例:
String str = “start”;
for (int i = 0; i < 100; i++) {
str = str + “hello”;
}

  • 25.【推荐】类成员与方法访问控制从严:

1) 如果不允许外部直接通过 new 来创建对象,那么构造方法必须是 private。
2) 工具类不允许有 public 或 default 构造方法。
3) 类非 static 成员变量并且与子类共享,必须是 protected。
4) 类非 static 成员变量并且仅在本类使用,必须是 private。
5) 类 static 成员变量如果仅在本类使用,必须是 private。
6) 若是 static 成员变量,考虑是否为 final。
7) 类成员方法只供类内部调用,必须是 private。
8) 类成员方法只对继承类公开,那么限制为 protected。
说明:任何类、方法、参数、变量,严控访问范围。过于宽泛的访问范围,不利于模块解耦。思考:如果
是一个 private 的方法,想删除就删除,可是一个 public 的 service 成员方法或成员变量,删除一下,不
得手心冒点汗吗?变量像自己的小孩,尽量在自己的视线内,变量作用域太大,无限制的到处跑,那么你
会担心的。

5.日期时间

  • 【强制】日期格式化时,传入 pattern 中表示年份统一使用小写的 y。

说明:日期格式化时,yyyy 表示当天所在的年,而大写的 YYYY 代表是 week in which year(JDK7 之后
引入的概念),意思是当天所在的周属于的年份,一周从周日开始,周六结束,只要本周跨年,返回的 YYYY就是下一年。
正例:表示日期和时间的格式如下所示:
new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”)

  • 【强制】在日期格式中分清楚大写的 M 和小写的 m,大写的 H 和小写的 h 分别指代的意义。

说明:日期格式中的这两对字母表意如下:
1) 表示月份是大写的 M;
2) 表示分钟则是小写的 m;
3) 24 小时制的是大写的 H;
4) 12 小时制的则是小写的 h。

  • 【强制】获取当前毫秒数:System.currentTimeMillis(); 而不是 new Date().getTime()。

说明:如果想获取更加精确的纳秒级时间值,使用 System.nanoTime 的方式。在 JDK8 中,针对统计时间等场景,推荐使用 Instant 类。

  • 【强制】不允许在程序任何地方中使用:1)java.sql.Date 2)java.sql.Time 3)

java.sql.Timestamp。
说明:第 1 个不记录时间,getHours()抛出异常;第 2 个不记录日期,getYear()抛出异常;第 3 个在构造方法 super((time/1000)*1000),fastTime 和 nanos 分开存储秒和纳秒信息。

6.集合处理

  • 【强制】判断所有集合内部的元素是否为空,使用isEmpty()方法,而不是size() == 0的方式。

说明:前者的时间复杂度为 O(1),而且可读性更好。

  1. Map<String, Object> map = new HashMap<>();
  2. if(map.isEmpty()) {
  3. System.out.println("no element in this map.");
  4. }
  • 【强制】在使用 java.util.stream.Collectors 类的 toMap()方法转为 Map 集合时,一定要使 用含有参数类型为 BinaryOperator,参数名为 mergeFunction 的方法,否则当出现相同 key 值时会抛出 IllegalStateException 异常。

说明:参数 mergeFunction 的作用是当出现 key 重复时,自定义对 value 的处理策略。
正例:

  1. List<Pair<String, Double>> pairArrayList = new ArrayList<>(3);
  2. pairArrayList.add(new Pair<>("version", 6.19));
  3. pairArrayList.add(new Pair<>("version", 10.24));
  4. pairArrayList.add(new Pair<>("version", 13.14));
  5. Map<String, Double> map = pairArrayList.stream().collect(
  6. // 生成的 map 集合中只有一个键值对:{version=13.14}
  7. Collectors.toMap(Pair::getKey, Pair::getValue, (v1, v2) -> v2));

反例:

  1. String[] departments = new String[] {"iERP", "iERP", "EIBU"};
  2. // 抛出 IllegalStateException 异常
  3. Map<Integer, String> map = Arrays.stream(departments)
  4. .collect(Collectors.toMap(String::hashCode, str -> str));
  • 【强制】使用 Map 的方法 keySet()/values()/entrySet()返回集合对象时,不可以对其进行添 加元素操作,否则会抛出UnsupportedOperationException 异常。
  • 【强制】在subList场景中,高度注意对父集合元素的增加或删除,均会导致子列表的遍历、增加、删除产生ConcurrentModificationException异常。

  • 【强制】使用集合转数组的方法,必须使用集合的toArray(T[] array),传入的是类型完全一致、长度为0的空数组。

反例:直接使用toArray无参方法存在问题,此方法返回值只能是Objectp[]类,若强转其他类型数组将出现,ClassCastException错误。
正例:

  1. List<String> list = new ArrayList<>(2);
  2. list.add("guan");
  3. list.add("bao");
  4. String[] array = list.toArray(new String[0]);

说明:使用 toArray 带参方法,数组空间大小的 length,
1) 等于 0,动态创建与 size 相同的数组,性能最好。
2) 大于 0 但小于 size,重新创建大小等于 size 的数组,增加 GC 负担。
3) 等于 size,在高并发情况下,数组创建完成之后,size 正在变大的情况下,负面影响与 2 相同。
4) 大于 size,空间浪费,且在 size 处插入 null 值,存在 NPE 隐患。

  • 【强制】在使用 Collection 接口任何实现类的 addAll()方法时,都要对输入的集合参数进行

NPE 判断。
说明:在 ArrayList#addAll 方法的第一行代码即 Object[] a = c.toArray(); 其中 c 为输入集合参数,如果
为 null,则直接抛出异常。

  • 【强制】使用工具类 Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的 add/remove/clear 方法会抛出 UnsupportedOperationException 异常。

说明:asList 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。Arrays.asList 体现的是适配
器模式,只是转换接口,后台的数据仍是数组。

  • 【强制】不要在foreach循环里进行元素的remove/add操作,remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁。

    1. //正例
    2. List<String> list = new ArrayList<>();
    3. list.add("1");
    4. list.add("2");
    5. Iterator<String> iterator = list.iterator();
    6. while (iterator.hasNext()) {
    7. String item = iterator.next();
    8. if (删除元素的条件) {
    9. iterator.remove();
    10. }
    11. }
    12. //反例:
    13. for (String item : list) {
    14. if ("1".equals(item)) {
    15. list.remove(item);
    16. }
    17. }
  • 【强制】在 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 比较结果相同。
反例:下例中没有处理相等的情况,交换两个对象判断结果并不互反,不符合第一个条件,在实际使用中
可能会出现异常。

  1. new Comparator<Student>() {
  2. @Override
  3. public int compare(Student o1, Student o2) {
  4. return o1.getId() > o2.getId() ? 1 : -1;
  5. }
  6. };

7.并发处理

8.控制语句

  • 【强制】在一个 switch 块内,每个 case 要么通过 continue/break/return 等来终止,要么注释说明程序将继续执行到哪一个 case 为止;在一个 switch 块内,都必须包含一个 default语句并且放在最后,即使它什么代码也没有。

说明:注意break是退出switch语句块,而return是退出方法题

  • 【强制】当 switch 括号内的变量类型为 String 并且此变量为外部参数时,必须先进行 null判断。

反例:如下的代码输出的是什么?

  1. public class SwitchString {
  2. public static void main(String[] args) {
  3. method(null);
  4. }
  5. public static void method(String param) {
  6. switch (param) {
  7. // 肯定不是进入这里
  8. case "sth":
  9. System.out.println("it's sth");
  10. break;
  11. // 也不是进入这里
  12. case "null":
  13. System.out.println("it's null");
  14. break;
  15. // 也不是进入这里
  16. default:
  17. System.out.println("default");
  18. }
  19. }
  20. }
  • 【强制】在 if/else/for/while/do 语句中必须使用大括号。

说明:即使只有一行代码,禁止不采用大括号的编码方式:if (condition) statements;

  • 【强制】三目运算符 condition? 表达式 1 : 表达式 2 中,高度注意表达式 1 和 2 在类型对齐时,可能抛出因自动拆箱导致的 NPE 异常。

说明:以下两种场景会触发类型对齐的拆箱操作:
1)表达式1或表达式2的值只要有一个是原始类型。
2)表达式1或表达式2的值的类型不一致,会强制拆箱升级成表示范围更大的那个类型。
反例:

  1. Integer a = 1;
  2. Integer b = 2;
  3. Integer c = null;
  4. Boolean flag = false;
  5. // a*b 的结果是 int 类型,那么 c 会强制拆箱成 int 类型,抛出 NPE 异常
  6. Integer result=(flag? a*b : c);
  • 【推荐】除常用方法(如 getXxx/isXxx)等外,不要在条件判断中执行其它复杂的语句,将复杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性。

说明:很多if语句内的逻辑表达式相当复杂,与、或、取反混合运算,甚至各种方法纵深调用,理解成本非常高,如果赋值一个非常好理解的布尔变量名字,则是件令人赏心悦目的事情。

  1. 正例:
  2. // 伪代码如下
  3. final boolean existed = (file.open(fileName, "w") != null) && (...) || (...);
  4. if (existed) {
  5. ...
  6. }
  7. 反例:
  8. public final void acquire ( long arg){
  9. if (!tryAcquire(arg) &&
  10. acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) {
  11. selfInterrupt();
  12. }
  • 【推荐】不要在其它表达式(尤其是条件表达式)中,插入赋值语句。

说明:赋值点类似于人体的穴位,对于代码的理解至关重要,所以赋值语句需要清晰地单独成为一行。

  1. 反例:
  2. public Lock getLock(boolean fair) {
  3. // 算术表达式中出现赋值操作,容易忽略 count 值已经被改变
  4. threshold = (count = Integer.MAX_VALUE) - 1;
  5. // 条件表达式中出现赋值操作,容易误认为是 sync==fair
  6. return (sync = fair) ? new FairSync() : new NonfairSync();
  7. }
  • 【推荐】循环体中的语句要考量性能,以下操作尽量移至循环体外处理,如定义对象、变量、获取数据库连接,进行不必要的 try-catch 操作(这个 try-catch 是否可以移至循环体外)


  • 【推荐】避免采用取反逻辑运算符。

说明:取反逻辑不利于快速理解,并且取反逻辑写法必然存在对应的正向逻辑写法。



9.注释规约

  • 【强制】类、类属性、类方法的注释必须使用 Javadoc 规范,使用/*内容/格式,不得使用// xxx 方式。

说明:在 IDE 编辑窗口中,Javadoc 方式会提示相关注释,生成 Javadoc 可以正确输出相应注释;在 IDE
中,工程调用方法时,不进入方法即可悬浮提示方法、参数、返回值的意义,提高阅读效率。

  • 【强制】所有的类都必须添加创建者和创建日期。

说明:在设置模板时,注意 IDEA 的@author 为${USER},而 eclipse 的@author 为${user},大小写有
区别,而日期的设置统一为 yyyy/MM/dd 的格式。

  1. 正例:
  2. /**
  3. * @author yangguanbao
  4. * @date 2016/10/31
  5. */
  • 【强制】方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释使用/ /注释,注意与代码对齐。


  • 【强制】所有的枚举类型字段必须要有注释,说明每个数据项的用途。

  • 【推荐】代码修改的同时,注释也要进行相应的修改,尤其是参数、返回值、异常、核心逻辑等的修改。

说明:代码与注释更新不同步,就像路网与导航软件更新不同步一样,如果导航软件严重滞后,就失去了
导航的意义。

异常日志

1.错误码:

  • 【强制】错误码的制定原则:快速溯源、简单易记、沟通标准化。

说明: 错误码想得过于完美和复杂,就像康熙字典中的生僻字一样,用词似乎精准,但是字典不容易随身
携带并且简单易懂。
正例:错误码回答的问题是谁的错?错在哪?1)错误码必须能够快速知晓错误来源,可快速判断是谁的问
题。2)错误码易于记忆和比对(代码中容易 equals)。3)错误码能够脱离文档和系统平台达到线下轻量
化地自由沟通的目的。

  • 【强制】错误码不体现版本号和错误等级信息。

说明:错误码以不断追加的方式进行兼容。错误等级由日志和错误码本身的释义来决定。

2.异常处理

  • 【强制】Java 类库中定义的可以通过预检查方式规避的 RuntimeException 异常不应该通过catch 的方式来处理,比如:NullPointerException,IndexOutOfBoundsException 等等。

说明:无法通过预检查的异常除外,比如,在解析字符串形式的数字时,可能存在数字格式错误,不得不
通过 catch NumberFormatException 来实现。
正例:if (obj != null) {…}
反例:try { obj.method(); } catch (NullPointerException e) {…}

  • 【强制】捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的内容。

单元测试

安全规约

MySQL数据库

工程结构

设计规约