# 代码规范

# 1.代码风格

# 1.1缩进

Indent设置为4(表示按一下tab键代表4个空格)

dock

# 1.2行数(不包括注释)

函数:最大长度-->300行-->提取子函数

类:最大长度-->1500行-->重构至其他类,再进行调用

# 1.3页宽

建议设置100

dock

超长语句应在一个逗号或一个操作符前做换行处理:换行后应比原语句缩进一个TAB或4个空格

# 1.4换行、空格

1)大括号内若为空:{}

​ 非空:

​ 左大括号前不换行,左大括号后换行

​ 右大括号前换行,右大括号后还有else等代码则不换行

​ 表示终止右大括号后必须换行。

2)左括号和后一个字符之间不出现空格;同样,右括号和前一个字符之间也不出现空格。

3)if/for/while/switch/do等保留字与左右括号之间都必须加空格。

public void test(){//左大括号前不换行,左大括号后换行
    //运算符的左右必须有一个空格
    boolean flag = true;
    //关键字if与括号之间必须有一个空格,括号里的不需要有空格
    if (flag) {
        System.out.println(flag);
        //右大括号前换行,右大括号后还有else等代码则不换行
    } else {
        System.out.println(flag);
    //表示终止右大括号后必须换行。  
    }
    
}//右大括号前换行
1
2
3
4
5
6
7
8
9
10
11
12
13
dock

# 1.5括号

​ 为提高可读性,if、for必须加括号

# 1.6字符集

​ UTF-8

# 2.注释

# 2.1基本原则

TIP

注释应该增加代码的清晰度。代码注释的目的是要使代码更易于被其他开发人员等理解

避免使用装饰性内容

保持注释的简洁

注释信息不仅要包括代码的功能,还应给出原因

不要为注释而注释

除变量定义等较短语句的注释外,避免使用行尾注释

好的命名、代码结构是自解释的,注释力求精简准确、表达到位

1.注释掉的代码尽量要配合说明,而不是简单的注释掉

说明:代码被注释掉有两种可能性:1)后续会恢复此段代码逻辑。2)永久不用。前者如果没有备注信息,难以知晓注释动机。后者建议直接删掉(代码仓库保存了历史代码)

2.特殊注释

请注明标记人与标记时间。注意及时处理这些标记,通过标记扫描,经常清理此类标记。线上故障有时候就是来源于这些标记处的代码。

1) 待办事宜(TODO):( 标记人,标记时间,[预计处理时间]) 表示需要实现,但目前还未实现的功能。

2) 错误,不能工作(FIXME):(标记人,标记时间,[预计处理时间]) 在注释中用FIXME标记某代码是错误的,而且不能工作,需要及时纠正的情况。

# 2.2文件、包注释

在每个文件、包的头部都应该包含该文件的功能、作用、作者、版权以及创建、修改记录等。

# 2.3类、接口注释

在类、接口定义之前当对其进行注释,包括类、接口的目的、作用、功能、继承于何种父类,实现的接口、实现的算法、使用方法、示例程序等

dock
/**
 * @Author: mayuanyuan
 * @Date: ${YEAR}/${MONTH}/${DAY}/${TIME}
 * @Description: 
 */

/**
 * Created with IntelliJ IDEA.
 * @Auther: mayuanyuan
 * @Date: ${YEAR}/${MONTH}/${DAY}/${TIME}
 * @Description: 
 */


**
* @Description $description$
* @Param $params$
* @return $returns$
* @throws $throws$
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 2.6方法注释

TIP

依据标准JavaDoc规范对方法进行注释,以明确该方法功能、作用、各参数含义以及返回值等。复杂的算法用/**/在方法内注解出。

参数注释时当注明其取值范围等

返回值当注释出失败、错误、异常时的返回情况。

异常当注释出什么情况、什么时候、什么条件下会引发什么样的异常

左边这个*就是注释的快捷键 / +就是输入'/ 和Enter'注释就出来了

dock

# 2.7基本原则

规范的命名能使程序更易阅读,从而更易于理解。它们也可以提供一些标识功能方面的信息,有助于更好的理解代码和应用。

1.使用可以准确说明变量/字段/类/接口/包等的完整的英文描述符。例如,采用类似 firstName,listAllUsers 或 CorporateCustomer 这样的名字,严禁使用汉语拼音及不相关单词命名,虽然Java支持Unicode命名,但本规范规定对包、类、接口、方法、变量、字段等不得使用汉字等进行命名。

2.采用该领域的术语。如果用户称他们的“客户” (clients) 为“顾客” (customers),那么就采用术语 Customer 来命名这个类,而不用 Client。

3.采用大小写混合,提高名字的可读性。一般应该采用小写字母,但是类和接口的名字的首字母,以及任何中间单词的首字母应该大写。包名全部小写。

4.尽量少用缩写,但如果一定要使用,当使用公共缩写和习惯缩写等,如实现(implement)可缩写成impl,经理(manager)可缩写成mgr等,严禁滥用缩写。

5.避免使用长名字(最好不超过 25 个字母)。

6.避免使用相似或者仅在大小写上有区别的名字。

避免使用数字,但可用2代替to,用4代替for等,如:go2Jsp。

# 3.命名

# 3.1文件、包

1.文件名当与其类严格相同,所有单词首字母大写。

2.包名一般以项目或模块名命名,少用缩写和长名,一律小写。

3.基本包:org.skyinn,所有包、文件都从属于此包。

4.包名按如下规则组成:

[基本包].[公司名].[项目名].[模块名].[子模块名]...

不得将类直接定义在基本包下,所有项目中的类、接口等都当定义在各自的项目和模块包中。

包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用单数形式,但是类名如果有复数含义,类名可以使用复数形式

# 3.2类、接口

对于Service和DAO类,基于SOA的理念,暴露出来的服务一定是接口,内部的实现类用Impl的后缀与接口区别

1.常量

采用完整的英文大写单词,在词与词之间用下划线连接

如:

public final static String KEY_FLUSH_TIME = “flush_time”;

2.不允许出现任何魔法值(即未经定义的常量)直接出现在代码中。

反例: String key = "Id#exp_"+tradeId;

3.变量和参数

变量的访问应尽量由get、set访问方法实现。

4.集合

一个集合,命名应采用完整的英文描述符,名字中所有非开头的单词的第一个字母应大写,适当使用集合作为结尾。如:

Map productMap = new Map();

List userList = new ArrayList();

# 3.3方法

方法的命名应采用完整的英文描述符,大小写混合使用:所有中间单词的第一个字母大写。方法名称的第一个单词常常采用一个有强烈动作色彩的动词。

取值类使用get前缀,设值类使用set前缀,判断类使用is(has)前缀。

方法参数建议顺序:(被操作者,操作内容,操作标志,其他⋯)

例:

public void replace(String sourceStr, //源字串 

                    String oldStr, //被替换字串 

                    String newStr){ //替换为字串
    
}
1
2
3
4
5
6
7

# 4.类与接口

# 4.1基本原则

类的划分粒度,不可太大,造成过于庞大的单个类,也不可太细,从而使类的继承太深。一般而言,一个类只做一件事;另一个原则是根据每个类的职责进行划分,比如用User来存放用户信息,而用UserDAO来对用户信息进行数据访问操作(比如存取数据库),用UserService来封装用户信息的业务操作等等。

多使用设计模式,随时重构。

多个类中使用相同方法时将其方法提到一个接口中或使用抽象类,尽量提高重用度。

将不希望再被继承的类声明成final,例如某些实用类,但不要滥用final,否则会对系统的可扩展性造成影响。

将不希望被实例化的类的缺省构造方法声明成private

# 4.2抽象类与接口

一般而言:接口定义行为,而抽象类定义属性和公有行为,注意两者间的取舍,在设计中,可由接口定义公用的行为,由一个抽象类来实现其部分或全部方法,以给子类提供统一的行为定义,可参考Java集合等实现。

多使用接口,尽量做到面向接口的设计,以提高系统的可扩展性。

# 4.3继承与工具类

尽量使用工具类来代替继承,一则可以使类的层次不至于过深,而且会使类与类,包与包之间的耦合度更小,更具可扩展性。

# 4.4构造函数和生成工厂

当对象的生成逻辑比较复杂,或实例化后需要做后置处理的,或是生成运行需要遵循某一项规则时。则实例化时推荐使用工厂方法。

如:

public class Listener{

	public Listener(){}

	public void initialize();

}

public class ListenerFactory{              

  public static Listener createListener(){

	Listenrer lis = new Listener();

	lis.initialize();

	return lis;

  }  

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 5.方法

# 5.1基本原则

一个方法只完成一项功能,在定义系统的公用接口方法外的方法应尽可能的缩小其可见性。

避免用一个类是实例去访问其静态变量和方法。

避免在一个较长的方法里提供多个出口:

    //不要使用这钟方式,当处理程序段很长时将很难找到出口点 

    if(condition){ 

        return A; 

    }else{ 

        return B; 

    } 

    //建议使用如下方式 

    String result = null; 

    if(condition){ 

        result = A; 

    }else{ 

        result = B; 

    } 

    return result; 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 5.2参数和返回值

避免过多的参数列表,尽量控制在5个以内,若需要传递多个参数时,当使用一个容纳这些参数的对象进行传递,以提高程序的可读性和可扩展性。

当方法参数比较多时,推荐使用VO对象。

/**

 * 代表多个参数的VO对象

 */

public class ParamVO{

  protected String param;

  protected String param2;..

}

public void function(ParamVO vo){}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 6.表达式语句

# 6.1基本原则

表达式和语句当清晰、简洁,易于阅读和理解,避免使用晦涩难懂的语句。

每行至多包含一条执行语句,过长当换行。

避免在构造方法中执行大量耗时的初始化工作,应当将这中工作延迟到被使用时再创建相应资源,如果不可避免,则当使用对象池和Cache等技术提高系统性能。

避免在一个语句中给多个变量赋相同的值。它很难读懂。

尽量在声明局部变量的同时初始化。唯一不这么做的理由是变量的初始值依赖于某些先前发生的计算。

一般而言,在含有多种运算符的表达式中使用圆括号来避免运算符优先级问题,是个好方法。即使运算符的优先级对你而言可能很清楚,但对其他人未必如此。你不能假设别的程序员和你一样清楚运算符的优先级。

不要为了表现编程技巧而过分使用技巧,简单就好。

# 6.2控制语句

判断中如有常量,则应将常量置与判断式的右侧。

如:

    if ( true == isAdmin())... 

    if ( null == user)... 

    所有if语句必须用{}包括起来,即便是只有一句:

    if (true){ 

    //do something...... 

    } 

    if (true) 

    i = 0; //不要使用这种 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

除常用方法(如getXxx/isXxx)等外,不要在条件判断中执行其它复杂的语句,将复杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性。 说明:很多if语句内的逻辑相当复杂,阅读者需要分析条件表达式的最终结果,才能明确什么样的条件执行什么样的语句,那么,如果阅读者分析逻辑表达式错误呢?

//正例: 
//伪代码如下 

boolean existed = (file.open(fileName, "w") != null) && (...) || (...); 

if (existed) { 

... 

} 

//反例:

if ((file.open(fileName, "w") != null) && (...) || (...)) { 

... 

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 6.3循环语句

循环中必须有终止循环的条件或语句,避免死循环。

因为循环条件在每次循环中多会执行一次,故尽量避免在其中调用耗时或费资源的操作,比较一下两种循环的差异:

    //不推荐方式____________________________________________

    while(index < products.getCount()){ 

        //每此都会执行一次getCount()方法, 

        //若此方法耗时则会影响执行效率 

        //而且可能带来同步问题,若有同步需求,请使用同步块或同步方法

    } 

    //推荐方式______________________________________________

    //将操作结构保存在临时变量里,减少方法调用次数 

	final int count = products.getCount(); 

	while(index < count){ 

	} 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 7.性能与安全

# 7.1基本原则

性能的提升并不是一蹴而就的,而是由良好的编程积累的,虽然任何良好的习惯和经验所提升的性能都十分有限,甚至微乎其微,但良好的系统性能却是由这些习惯等积累而成,不积细流,无以成江海!

# 7.2 String与StringBuffer

不要使用如下String初始化方法:

String str = new String(“abcdef”);

这将产生两个对象,应当直接赋值:

String str = “abcdef”;

在处理可变 String 的时候要尽量使用 StringBuffer 类,StringBuffer 类是构成 String 类的基础。String 类将 StringBuffer 类封装了起来,(以花费更多时间为代价)为开发人员提供了一个安全的接口。当我们在构造字符串的时候,我们应该用 StringBuffer 来实现大部分的工作,当工作完成后将 StringBuffer 对象再转换为需要的 String 对象。比如:如果有一个字符串必须不断地在其后添加许多字符来完成构造,那么我们应该使用 StringBuffer 对象和她的 append() 方法。如果我们用 String 对象代替 StringBuffer 对象的话,将会花费许多不必要的创建和释放对象的 CPU 时间。

# 7.3 集合

避免使用Vector和HashTable等旧的集合实现,这些实现的存在仅是为了与旧的系统兼容,而且由于这些实现是同步的,故而在大量操作时会带来不必要的性能损失。在新的系统设计中不当出现这些实现,使用ArrayList代替Vector,使用HashMap代替HashTable。

若确实需要使用同步集合类,当使用如下方式获得同步集合实例:

Map map = Collections.synchronizedMap(new HashMap());

由于数组、ArrayList与Vector之间的性能差异巨大,故在能使用数组时不要使用ArrayList,尽量避免使用Vector。

# 7.4 垃圾收集和资源释放

不要过分依赖JVM的垃圾收集机制,因为你无法预测和知道JVM在什么时候运行GC。

尽可能早的释放资源,不再使用的资源请立即释放。

可能有异常的操作时必须在try的finally块中释放资源,如数据库连接、IO操作等:

Connection conn = null; 
try{ 
	//do something 
}catch(Exception e){ //异常捕捉和处理 
	e.printStackTrack(); 
}finally{ 
//判断conn等是否为null 
	if(null != conn){ 
		conn.close(); 
	} 
}//end try...catch...finally 

1
2
3
4
5
6
7
8
9
10
11
12

# 8.数据库兼容(oracle&mysql)

1.在 Oracle中sql语句不区分大小写,而在mysql中列名不区分大小写,但是表名区分大小写, 所以我们在编写的sql语句时统一表名小写

2.在设计表的时候,唯一id尽量使用uuid,而不采用自增长id

3.as在oracle中只能用于列别名,不能用于表别名;为保持兼容性,建议在表名后均不加as关键字

4.字符串用统一用单引号包裹

5.字符串拼接统一使用||,避免使用concat函数

6.在没办法兼容二者的情况下,比如在使用某些数据库特有函数时,请在对应的java方法中添加相关注释// 存在oracle&mysql兼容问题

TIP

oracle和mysql不兼容的方法 dock

# 9.其他

1.静态变量或静态方法直接使用类名访问

2.重写方法必须加@Override注解

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

正例:public User getUsers(String type, Integer... ids)

TIP

java 可变参数是bai1.5版本的新特性,也du就是说用户若是想定义一个方法,zhi但是在此之前并不知道dao以后要用的时候想传几个参数进去,可以在方法的参数列表中写参数类型或者数组名,然后在方法内部直接用操作数组的方式操作。

Java1.5版本的可变参数注意事项如下:

1、重写方法不能缩小访问权限。

2、参数列表必须与被重写方法相同。

3、返回类型必须与被重写方法的相同或是其子类。 4、重写方法不能抛出新的异常,或者超过了父类范围的异常,但是可以抛出更少和更有限的异常,或者不抛出异常。

4.不能使用过时的类或方法。

说明:java.net.URLDecoder 中的方法decode(String encodeStr) 这个方法已经过时,应该使用双参数decode(String source, String encode)。接口提供方既然明确是过时接口,那么有义务同时提供新的接口;作为调用方来说,有义务去考证过时方法的新实现是什么。

5.Object的equals方法容易抛空指针异常,应使用常量或确定有值的对象来调用equals。 正例:

"test".equals(object);

反例:

object.equals("test");

JDK7引入的工具类

java.util.Objects#equals

6.所有的相同类型的包装类对象之间值的比较,全部使用equals方法比较。

说明:对于Integer var = ?在-128至127之间的赋值,Integer对象是在IntegerCache.cache产生,会复用已有对象,这个区间内的Integer值可以直接使用==进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用equals方法进行判断。

7.循环体内,字符串的连接方式,使用StringBuilder的append方法进行扩展。

反例:

String str = "start";

for (int I = 0; I < 100; i++) {

str = str + "hello";

}

说明:反编译出的字节码文件显示每次循环都会new出一个StringBuilder对象,然后进行append操作,最后通过toString方法返回String对象,造成内存资源浪费。

8.表名、字段名必须使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间只出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。

正例:

getter_admin,task_config,level3_name

反例:

GetterAdmin,taskConfig,level_3_name

9.表名不使用复数名词。 说明:表名应该仅仅表示表里面的实体内容,不应该表示实体数量,对应于DO类名也是单数形式,符合表达习惯。表名一般不超过四个单词,以下划线分隔,中间关系表,以relate结尾

正例:

base_user, base_system_parameter, base_user_role_relate

10.主键索引名为pk_字段名;唯一索引名为uk_字段名;普通索引名则为idx_字段名。 说明:pk_ 即primary key;uk_ 即 unique key;idx_ 即index的简称。

11.表必备字段:id creator gmt_create modifier gmt_modified

字段允许适当冗余,以提高性能,但是必须考虑数据同步的情况。冗余字段应遵循: 1)不是频繁修改的字段。 2)不是varchar超长字段,更不能是text字段。

12.对trace/debug/info级别的日志输出,必须使用使用占位符的方式。

说明:logger.debug("Processing trade with id: " + id + " symbol: " + symbol); 如果日志级别是warn,上述日志不会打印,但是会执行字符串拼接操作,如果symbol是对象,会执行toString()方法,浪费了系统资源,执行了上述操作,最终日志却没有打印。

正例:

logger.debug("Processing trade with id: {} symbol : {} ", id, symbol);

13.异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么通过关键字throws往上抛出。

正例:

logger.error("设备创建异常:" + e.getMessage(), e);