JAVA开发规范
1 方法行数不能超过50行,特殊情况除外;说明:如果行数过长不利于整个方法的阅读理解。如果是笔记本建议控制在40以内。
2 方法参数不能多于7个,特殊情况除外;说明:参数过多不利于整个方法的阅读理解。
3 变量名称需有意义且符合驼峰式写法,严禁出现a,b,c 等字符作为变量名称。
4 严禁出现魔鬼数字(在代码中没有具体含义的数字、字符串)。
5 新建类名,方法名意义尽量切近当前功能模块的功能,且符合驼峰式写法。
6 If语句后必须写花括号,严禁出现 if(true)后面直接写代码,需带花括号{}。
7 条件判断中必须有空格 如 if (1 == 2)。 等号两边需加空格。
8 严禁使用==判断字符串值,需使用equals方法判断。
9 与null对象进行判断, 需写成(null == xxx) 形式,null写在前;说明:有时候如果将变量写在前,恰好将==疏忽写成= 就会造成 xxx = null,等于是给变量赋值为null。
10 对当前对象进行null判断,并且进行调用对象方法判断时,形如if(null != xxx && xxx.size() > 0)需将null != xxx判断写在前,这样如果为null,可以直接短路,防止空指针异常产生。
11 静态常量名称应大写,多个单词可用下划线拼接,如 SELECT_NUMBER = 1。
12 方法注释,若当前方法功能较为复杂,需以javadoc方式注释进行说明,注释需写成形如
/**
*
* @author username
* @param oldStage
*/
13 无需再对boolean 值再进行判断,直接使用即可,如 if (success) { TODO…}。
反例: if (success == true) { TODO…}
14 POJO 类中的任何布尔类型的变量,都不要加 is,否则部分框架解析会引起序列化错误。反例:定义为基本数据类型 boolean isSuccess;的属性,它的方法也是 isSuccess(),RPC框架在反向解析的时候,“以为”对应的属性名称是 success,导致属性获取不到,进而抛出异常。
15 if/for/while/switch/do 等保留字与左右括号之间都必须加空格。任何运算符左右必须加一个空格。说明:运算符包括赋值运算符=、逻辑运算符&&、加减乘除符号、三目运行符等。
16 IDE 的 text file encoding 设置为 UTF-8。
17 Object的equals方法容易抛空指针异常,应使用常量或确定有值的对象来调用equals。
例如: "test".equals(object)
18 POJO 类必须写 toString 方法。使用工具类 source> generate toString 时,如果继
承了另一个 POJO 类,注意在前面加一下 super.toString。说明:在方法执行抛出异常时,可以直接调用 POJO 的 toString()方法打印其属性值,便于排查问题。
19 使用索引访问用 String 的 split 方法得到的数组时,需做最后一个分隔符后有无内
容的检查,否则会有抛 IndexOutOfBoundsException 的风险。说明:
String str = "a,b,c,,"; String[] ary = str.split(",");
//预期大于 3,结果是 3
System.out.println(ary.length);
20 【推荐】当一个类有多个构造方法,或者多个同名方法,这些方法应该按顺序放置在一起,便于阅读。
21 【推荐】 类内方法定义顺序依次是:公有方法或保护方法 > 私有方法 > getter/setter
方法。
22 【推荐】setter 方法中,参数名称与类成员变量名称一致,this.成员名=参数名。在
getter/setter 方法中,尽量不要增加业务逻辑,增加排查问题难度。
反例:
public Integer getData(){
if(true) {
return data + 100;
} else {
return data - 100;
}
}
23 【推荐】循环体内,字符串的联接方式,使用 StringBuilder 的 append 方法进行扩展。
反例:
String str = "start";
for(int i=0; i<100; i++){
str = str + "hello";
}
说明:反编译出的字节码文件显示每次循环都会 new 出一个 StringBuilder 对象,然后进行append 操作,最后通过 toString 方法返回 String 对象,造成内存资源浪费。
24 【推荐】final 可提高程序响应效率,声明成 final 的情况:
1) 不需要重新赋值的变量,包括类属性、局部变量。
2) 对象参数前加 final,表示不允许修改引用的指向。
3) 类方法确定不允许被重写。
25 Map/Set 的 key 为自定义对象时,必须重写 hashCode 和 equals。正例:String 重写hashCode 和 equals 方法,所以我们可以非常愉快地使用 String 对象作为 key 来使用。
26 使用集合转数组的方法,必须使用集合的 toArray(T[] array),传入的是类型完全
一样的数组,大小就是 list.size()。
反例:直接使用 toArray 无参方法存在问题,此方法返回值只能是 Object[]类,若强转其它类型数组将出现 ClassCastException 错误。
正例:
List<String> list = new ArrayList<String>(2);
list.add("jun");
list.add("jiang");
String[] array = new String[list.size()];
array = list.toArray(array);
说明:使用 toArray 带参方法,入参分配的数组空间不够大时,toArray 方法内部将重新分配内存空间,并返回新数组地址;如果数组元素大于实际所需,下标为[ list.size() ]的数组元素将被置为 null,其它数组元素保持原值,因此最好将方法入参数组大小定义与集合元素个数一致
27 不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator
方式,如果并发操作,需要对 Iterator 对象加锁。
反例:
List<String> a = new ArrayList<String>();
a.add("1");
a.add("2");
for (String temp : a) {
if("1".equals(temp)){
a.remove(temp);
}
}
说明:这个例子的执行结果会出乎大家的意料,那么试一下把“1”换成“2”,会是同样的结果吗?
正例:
Iterator<String> it = a.iterator();
while(it.hasNext()){
String temp = it.next();
if(删除元素的条件){
it.remove();
}
}
28 【推荐】集合初始化时,尽量指定集合初始值大小。说明:ArrayList 尽量使用 ArrayList(int initialCapacity) 初始化。
29 【推荐】使用 entrySet 遍历 Map 类集合 KV,而不是 keySet 方式进行遍历。
说明:keySet 其实是遍历了 2 次,一次是转为 Iterator 对象,另一次是从 hashMap 中取出 key所对应的 value。而 entrySet 只是遍历了一次就把 key 和 value 都放到了 entry 中,效率更高。如果是 JDK8,使用 Map.foreach 方法。
正例:values()返回的是 V 值集合,是一个 list 集合对象;keySet()返回的是 K 值集合,是一个 Set 集合对象;entrySet()返回的是 K-V 值组合集合。
30 SimpleDateFormat 是线程不安全的类,一般不要定义为 static 变量,如果定义为static,必须加锁,或者使用 DateUtils 工具类。
正例:注意线程安全,使用 DateUtils。亦推荐如下处理:
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
说明:如果是 JDK8 的应用,可以使用 instant 代替 Date,Localdatetime 代替 Calendar,
Datetimeformatter 代替 Simpledateformatter,官方给出的解释:simple beautiful strong
immutable thread-safe。
31 高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能
锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。
32 多线程并行处理定时任务时,Timer 运行多个 TimeTask 时,只要其中之一没有捕获
抛出的异常,其它任务便会自动终止运行,使用 ScheduledExecutorService 则没有这个问题。
33 创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。
34 在一个 switch 块内,每个 case 要么通过 break/return 来终止,要么注释说明程序
将继续执行到哪一个 case 为止;在一个 switch 块内,都必须包含一个 default 语句并且放在最后,即使它什么代码也没有。
35 循环体中的语句要考量性能,以下操作尽量移至循环体外处理,如定义对象、变量、获取数据库连接,进行不必要的 try-catch 操作(这个 try-catch 是否可以移至循环体外)。
36 所有的抽象方法(包括接口中的方法)必须要用 javadoc 注释、除了返回值、参数、
异常说明外,还必须指出该方法做什么事情,实现什么功能。
说明:如有实现和调用注意事项,请一并说明。
37 所有的类都必须添加创建者信息。
38 所有的枚举类型字段必须要有注释,说明每个数据项的用途。
39 代码修改的同时,注释也要进行相应的修改,尤其是参数、返回值、异常、核心逻辑等的修改。
说明:代码与注释更新不同步,就像路网与导航软件更新不同步一样,如果导航软件严重滞后,就失去了导航的意义。
40 【参考】注释掉的代码尽量要配合说明,而不是简单的注释掉。
说明:代码被注释掉有两种可能性:1)后续会恢复此段代码逻辑。2)永久不用。前者如果没有备注信息,难以知晓注释动机。后者建议直接删掉(代码仓库保存了历史代码)。
41 【参考】对于注释的要求:第一、能够准确反应设计思想和代码逻辑;第二、能够描述业务含义,使别的程序员能够迅速了解到代码背后的信息。完全没有注释的大段代码对于阅读者形同天书,注释是给自己看的,即使隔很长时间,也能清晰理解当时的思路;注释也是给继任者看的,使其能够快速接替自己的工作。
42 错误,不能工作(FIXME):(标记人,标记时间,[预计处理时间])在注释中用 FIXME 标记某代码是错误的,而且不能工作,需要及时纠正的情况。
43 在使用正则表达式时,利用好其预编译功能,可以有效加快正则匹配速度。
说明:不要在方法体内定义:Pattern pattern = Pattern.compile(规则);
44 注意 Math.random() 这个方法返回是 double 类型,注意取值范围 0≤x<1(能够取到零值,注意除零异常),如果想获取整数类型的随机数,不要将 x 放大 10 的若干倍然后取整,直接使用 Random 对象的 nextInt 或者 nextLong 方法。
45 获取当前毫秒数:System.currentTimeMillis(); 而不是 new Date().getTime();
说明:如果想获取更加精确的纳秒级时间值,用 System.nanoTime。在 JDK8 中,针对统计时间等场景,推荐使用 Instant 类。
46 【推荐】任何数据结构的使用都应限制大小。说明:这点很难完全做到,但很多次的故障都是因为数据结构自增长,结果造成内存被吃光。
47 【推荐】对于“明确停止使用的代码和配置”,如方法、变量、类、配置文件、动态配置属性等要坚决从程序中清理出去,避免造成过多垃圾。清理这类垃圾代码是技术气场,不要有这样的观念:“不做不错,多做多错”。
48 不要捕获 Java 类库中定义的继承自 RuntimeException 的运行时异常类,如:
IndexOutOfBoundsException / NullPointerException,这类异常由程序员预检查来规避,保证程序健壮性。
正例: if(obj != null) {...}
反例: try { obj.method() } catch(NullPointerException e){…}
49 异常不要用来做流程控制,条件控制,因为异常的处理效率比条件分支低。
50 finally 块必须对资源对象、流对象进行关闭,有异常也要做 try-catch。
说明:如果 JDK7,可以使用 try-with-resources 方法。
51 不能在 finally 块中使用 return,finally 块中的 return 返回后方法结束执行,不
会再执行 try 块中的 return 语句。
52 避免出现重复的代码(Don’t Repeat Yourself),即 DRY 原则。
说明:随意复制和粘贴代码,必然会导致代码的重复,在以后需要修改时,需要修改所有的副本,容易遗漏。必要时抽取共性方法,或者抽象公共类,甚至是共用模块。
正例:一个类中有多个 public 方法,都需要进行数行相同的参数校验操作,这个时候请抽取:
private boolean checkParam(String str){...}
53 日志打印输出的 POJO 类必须重写 toString 方法,否则只输出此对象的 hashCode 值(地址值),没啥参考意义。
54 大量地输出无效日志,不利于系统性能提升,也不利于快速定位错误点。纪录日志时请
思考:这些日志真的有人看吗?看到这条日志你能做什么?能不能给问题排查带来好处?
55 中括号是数组类型的一部分,数组定义如下:String[] args;
反例:请勿使用 String args[]的方式来定义
56 抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类命
名以它要测试的类的名称开始,以 Test 结尾。