# 代码规范
# 1.代码风格
# 1.1缩进
Indent设置为4(表示按一下tab键代表4个空格)
# 1.2行数(不包括注释)
函数:最大长度-->300行-->提取子函数
类:最大长度-->1500行-->重构至其他类,再进行调用
# 1.3页宽
建议设置100
超长语句应在一个逗号或一个操作符前做换行处理:换行后应比原语句缩进一个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);
//表示终止右大括号后必须换行。
}
}//右大括号前换行
2
3
4
5
6
7
8
9
10
11
12
13
# 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类、接口注释
在类、接口定义之前当对其进行注释,包括类、接口的目的、作用、功能、继承于何种父类,实现的接口、实现的算法、使用方法、示例程序等
/**
* @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$
*/
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 2.6方法注释
TIP
依据标准JavaDoc规范对方法进行注释,以明确该方法功能、作用、各参数含义以及返回值等。复杂的算法用/**/在方法内注解出。
参数注释时当注明其取值范围等
返回值当注释出失败、错误、异常时的返回情况。
异常当注释出什么情况、什么时候、什么条件下会引发什么样的异常
左边这个*就是注释的快捷键 / +就是输入'/ 和Enter'注释就出来了
# 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){ //替换为字串
}
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;
}
}
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;
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){}
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; //不要使用这种
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) && (...) || (...)) {
...
}
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){
}
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
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不兼容的方法
# 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);