常量定义

1.强制---不允许任何魔法值(在代码中直接出现的值,只有在这个数值记述的那部分代码中才能明确了解其含义)直接出现在代码中。魔法值会严重影响代码的可读性和可维护性。

2.强制---long 或者 Long 初始赋值时,使用大写的L,不能是小写的l,小写容易跟数字1混淆,造成误解。

3.推荐---不使用同一个常量类维护所有常量,要按常量功能进行归类,分开维护。太大的常量类,不便于理解维护,甚至需要修改的常量需要查找功能才找的到。缓存相关常量放在类 CacheConsts 下;系统配置相关常量放在类 ConfigConsts 下。

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

  • 跨应用共享常量放置在二方库中,通常是client.jar中的constant目录下,二方库是公司内部的依赖库,一般指公司内部的其他项目发布的jar包。
  • 应用内共享常量在一方库中,指的是本项目或者本工程中的类和方法、接口等,通常是modules中的constant目录下。
  • 子工程内部共享常量,就是在当前子工程的constant目录下。
  • 包内共享常量,即当前包下单独的constant目录下。
  • 类内共享常量直接在类内部private static final定义。

5.推荐---如果变量值在一个范围内变化,且带有名称之外的延伸属性,应该定义为枚举类。延伸属性可以在构造器中定义,然后在常量名后添加。例如

enumColor {
    RED("红色", 1), GREEN("绿色", 2), BLUE("蓝色", 3);
    privatefinalStringname;
    privatefinalintindex;
    privateColor(Stringname, intindex) {
        this.name=name;
        this.index=index;
    }
}

代码格式

1.强制---大括号使用:大括号内容为空则不需要换行;如果是非空代码块则

1) 左大括号前不换行。2) 左大括号后换行。3) 右大括号前换行。4) 右大括号后还有 else 等代码则不换行;表示终止的右大括号后必须换行。

2.强制---左小括号和字符之间不出现空格;同样,右小括号和字符之间也不出现空格;if/for/while/switch/do 等保留字与括号之间都必须加空格;任何二目、三目运算符(赋值运算符=、逻辑运算符&&、加减乘除符号等)的左右两边都需要加一个空格;采用 4 个空格缩进,禁止使用 tab 字符。注释的双斜线与注释内容之间有且仅有一个空格

3.强制---单行字符数限制不超过 120 个,超出需要换行,换行时遵循如下原则

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

4.强制---方法参数在定义和传入时,多个参数逗号后边必须加空格。例:

method("a", "s", "d")
method(int index, String name);

5.强制---IDE 的 text file encoding 设置为 UTF-8; IDE 中文件的换行符使用 Unix 格式,不要使用 Windows 格式

6.没必要增加空格来对齐相应位置的字符;方法体内的执行语句组、变量的定义语句组、不同的业务逻辑之间或者不同的语义之间插入一个空行。相同业务逻辑和语义之间不需要插入空行

OOP规约

1.强制---避免通过一个类的对象引用访问此类的静态变量或静态方法,这样会增加编译器解析成本,也可能会使代码更难阅读和理解;直接用类名访问。静态变量和静态方法属于类,而不属于类的任何一个对象。这意味着,无论你创建了多少个类的对象,它们都共享同一个静态变量和静态方法。例如:int a = MyClass.x; MyClass.myMethod();

2.强制---所有的覆写方法,必须加@Override 注解。

3.强制---相同参数类型,相同业务含义,才可以使用 Java 的可变参数,避免使用 Object。可变参数必须放在参数列表的最后。

4.强制---外部正在调用或者二方库依赖的接口,不允许修改方法签名,避免对接口调用方产生影响。接口过时必须加@Deprecated注解,并清晰地说明采用的新接口或者新服务是什么。

Java9中 @Deprecated注解有两个新属性:forRemoval和since。其中,forRemoval是一个布尔类型的属性,指定该API在将来是否会被删除;而 since 是一个字符串类型的属性,指定该API从哪个版本被标记为过时。

5.强制---不使用过时的类或方法,接口提供方既然明确是过时接口,那么有义务同时提供新的接口;作为调用方来说,要去考证过时方法的新实现是什么。

6.强制---Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用equals。如object.equals(xxx)不可使用,应用xxx.equals(object)

//java.util.Objects.equals 可以避免抛出空指针异常
publicstaticbooleanequals(Objecta, Objectb) {
    return (a==b) || (a!=null&&a.equals(b));
}

7.强制---所有的相同类型的包装类对象之间值的比较,全部使用 equals 方法比较,规避128陷阱

8.关于基本数据类型与包装数据类型的使用标准

  • 强制---所有的 POJO 类属性必须使用包装数据类型
  • 强制---RPC 方法(远程调用协议)的返回值和参数必须使用包装数据类型,包装数据类型的null值能够表示额外的信息,如远程调用失败,异常退出等,避免NPE(NullPointerException的缩写)问题
  • 所有的局部变量使用基本数据类型

9.强制---定义DO/DTO/VO 等POJO类时,不要设定任何属性默认值

10.强制---序列化类新增属性时,请不要修改 serialVersionUID 字段,避免反序列失败;如果完全不兼容升级,避免反序列化混乱,那么请修改 serialVersionUID 值。serialVersionUID 不一致会抛出序列化运行时异常。

serialVersionUID是用于在序列化和反序列化过程中进行核验的一个版本号。它用来表明类的不同版本间的兼容性,其目的是以序列化对象进行版本控制,有关各版本反序加化时是否兼容.

11.强制---构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在init方法中。

12.强制---POJO 类必须写 toString 方法。使用 IDE 的中工具:source> generate toString时,如果继承了另一个 POJO 类,注意在前面加一下 super.toString。在方法执行抛出异常时,可以直接调用 POJO 的 toString()方法打印其属性值,便于排查问题。

集合处理

1.强制---关于 hashCode 和 equals 的处理,遵循如下规则

1) 只要重写 equals,就必须重写hashCode。
2) 因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的
对象必须重写这两个方法。
3) 如果自定义对象做为 Map 的键,那么必须重写 hashCode 和 equals。

2.强制---ArrayList的subList结果不可强转成ArrayList,否则会抛出ClassCastException异常,即 java.util.RandomAccessSubList cannot be cast to java.util.ArrayList.

subList 返回的是 ArrayList 的内部类 SubList,并不是 ArrayList,是ArrayList 的一个视图,对于 SubList 子列表的所有操作最终会反映到原列表上。

3.强制---在 subList 场景中,高度注意对原集合元素个数的修改,会导致子列表的遍历、增加、删除均会产生 ConcurrentModificationException(并发修改异常) 异常。

4.强制---使用集合转数组的方法,必须使用集合的 toArray(T[] array)(有参方法),传入的是类型完全一样的数组,大小就是 list.size(),无参方法返回值只能是Object[],强转其他类型将出现ClassCastException(类型强制转换异常)

ArrayList<String>strings=newArrayList<>();
strings.add("name");
strings.add("name");
String[] arr=strings.toArray(newString[strings.size()]);

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

6.强制---泛型通配符<? extends T>来接收返回的数据,此写法的泛型集合不能使用 add 方 法,而<? super T>不能使用 get 方法,做为接口调用赋值时易出错。

<? extends T> 泛型通配符表示类型参数可以是 T 或者 T 的子类。由于编译器无法确定具体的类型,所以不能使用 add 方法添加元素,因为这样可能会破坏集合的类型安全。
而 <? super T> 泛型通配符表示类型参数可以是 T 或者 T 的父类。由于编译器无法确定具体的类型,所以不能使用 get 方法获取元素,因为这样可能会破坏集合的类型安全。
PECS(Producer Extends Consumer Super)原则:第一、频繁往外读取内容的,适合用<? extends T>。第二、经常往里插入的,适合用<? super T>。

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

8.强制---在 JDK7 版本及以上,Comparator(比较器) 要满足如下三个条件,不然 Arrays.sort,Collections.sort 会报 IllegalArgumentException(非法参数)异常。

x,y 的比较结果和 y,x 的比较结果相反。
x>y,y>z,则 x>z
x=y,则 x,z 比较结果和 y,z 比较结果相同

并发处理

1.强制---获取单例对象需要保证线程安全,其中的方法也要保证线程安全。资源驱动类、工具类、单例工厂类都需要注意。因为单例对象会被多线程共享。

2.强制---创建线程或线程池时要指定有意义的线程名称,方便出错回溯。

3.强制---线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。:使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。

4.强制---线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

说明:Executors 返回的线程池对象的弊端如下:
1)FixedThreadPool 和 SingleThreadPool:允许的请求队列长度为 Integer.MAX_VALUE(2147.....21亿),可能会堆积大量的请求,从而导致 OOM(Out Of Memory,内存用完)。
2)CachedThreadPool 和 ScheduledThreadPool:允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

5.强制---SimpleDateFormat 是线程不安全的类,一般不要定义为 static 变量,如果定义为static,必须加锁,或者使用 DateUtils 工具类。如果是 JDK8 的应用,可以使用 Instant 代替 Date,LocalDateTime 代替 Calendar,DateTimeFormatter 代替 SimpleDateFormat。

6.强制---高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法体------锁越小越好;能用对象锁,就不要用类锁。尽可能使加锁的代码块工作量尽可能的小,避免在锁代码块中调用 RPC 方法。

7.强制---对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造成死锁。线程一需要对表 A、B、C 依次全部加锁后才可以进行更新操作,那么线程二的加锁顺序也必须是 A、B、C,否则可能出现死锁。

8.强制---并发修改同一记录时,避免更新丢失,需要加锁。要么在应用层加锁,要么在缓存加锁,要么在数据库层使用乐观锁,使用 version 作为更新依据。如果每次访问冲突概率小于 20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次数不得小于 3 次。

9.强制---多线程并行处理定时任务时,Timer 运行多个 TimeTask 时,只要其中之一没有捕获抛出的异常,其它任务便会自动终止运行,使用 ScheduledExecutorService 则没有这个问题。

控制语句

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

2.强制---在 if/else/for/while/do 语句中必须使用大括号。即使只有一行代码,避免采用单行的编码方式:

if (condition) statements;

注释规约

1.强制---类、类属性、类方法的注释必须使用 Javadoc 规范,使用/*内容/格式,不得使用// xxx 方式。说明:在 IDE 编辑窗口中,Javadoc 方式会提示相关注释,生成 Javadoc 可以正确输出相应注释;在 IDE 中,工程调用方法时,不进入方法即可悬浮提示方法、参数、返回值的意义,提高阅读效率。

2.强制---所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释、除了返回值、参数、异常说明外,还必须指出该方法做什么事情,实现什么功能。对子类的实现要求,或者调用注意事项,请一并说明。

3.强制---所有的类都必须添加创建者和创建日期。

4.强制---方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释使用/* */注释,注意与代码对齐。

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

其他

1.强制---在使用正则表达式时,利用好其预编译功能,可以有效加快正则匹配速度。不要在方法体内定义:Pattern pattern = Pattern.compile(规则);

2.强制---velocity 调用 POJO 类的属性时,建议直接使用属性名取值即可,模板引擎会自动按规范调用 POJO 的 getXxx(),如果是 boolean 基本数据类型变量(boolean 命名不需要加 is前缀),会自动调用 isXxx()方法。注意如果是 Boolean 包装类对象,优先调用 getXxx()的方法。

3.强制---后台输送给页面的变量必须加$!{var}——中间的感叹号。如果 var=null 或者不存在,那么${var}会直接显示在页面上

4.强制---注意 Math.random() 这个方法返回是 double 类型,注意取值的范围 0≤x<1(能够取到零值,注意除零异常),如果想获取整数类型的随机数,不要将 x 放大 10 的若干倍然后取整,直接使用 Random 对象的 nextInt 或者 nextLong 方法。

5.强制---获取当前毫秒数 System.currentTimeMillis(); 而不是 new Date().getTime(); 如果想获取更加精确的纳秒级时间值,使用 System.nanoTime()的方式。在 JDK8 中,针对统计时间等场景,推荐使用 Instant 类。