一命名风格
1、代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结尾。
反例:_name / $name / name_ / name$
2、类名使用UpperCamelCase风格,但以下情形例外:DO/BO/DTO/VO/AO/PO等
正例:MarcoPolo/UserDO/XmlService
反例:macroPolo/userDo/XMLService
3、常量命名全部用大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌弃名字长。
正例:MAX_STCK_COUNT
4、抽象类命名使用Abstract或Base开头;异常类命名使用Exception结尾;测试类命名以它要测试的类的名称开头,以Test结尾。
5、POJO类中布尔类型的变量,都不要加is,否则部分框架解析会英气序列化错误。
反例:定义为基本数据类型Boolean isDeleted;的属性,它的方法也是isDeleted();框架在反向解析的时候,“以为”对应的属性名称是deleted,导致属性获取不到,进而抛出异常。
6、包名统一使用小写,点分隔之间有且仅有亿个自然语义的英语单词。包名统一使用单数形式,但是类名如果有复数含义,类名可以使用复数形式。
正例:应用工具类包名为:com.alibaba.open.util 、类名为MessageUtils(此规则参考spring)
7、如果模块、接口、类、方法使用了设计模式,在命名时体现出具体模式。说明:将设计模式体现在名字中,有利于阅读者快速理解架构设计理念。
正例:pubic class OrderFactory
Public class LoginProxy
8、杜绝完全不规范的缩写,避免望文不知义;
反例:AbstractClass 缩写命名成AbsClass;condition缩写命名成condi,此类随意缩写严重降低了代码的可阅读性。
9、接口类中的方法和属性不要加任何修饰符(public也不要加),保持代码的简洁性,并加上有效的javadoc注释,尽量不要在接口定义变量,如果一定要定义变量,肯定是与接口方法有关,并且是整个应用的基础变量。
正例:接口方法签名:void f();
接口基础常量表示:String COMPANY="alibaba";
说明:JDK中接口允许有默认实现,那么这个default方法,是对所有实现类都有价值的默认实现。
10、枚举类名建议带上Enum后缀,枚举成员名称需要全大写,单词间用下划线隔开。
说明:枚举其实就是特殊的常量类,且构造方法默认强制私有。
正例:枚举名字为ProcessStatusEnum的成员名称:SUCCESS/ UNKNOWN_REASON。
11、各层命名规约(做参考)
A)Service/DAO层方法命名规约
1)获取打个对象的方法用get前缀
2)获取多个对象的方法用list做前缀
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
二 常量定义
1、不允许任何魔法值(即未经定义的常量)直接出现在代码中。
反例:String key="Id#taobao_"+tradeId;
cache.put(key,value);
2、long或者Long初始赋值时,使用大写L,不能小写的l,小写容易与数字1混淆,造成误解。
说明:Long a=2l;写的是数字21还是Long型2?
3、不要使用一个常量类维护所有常量,按常量功能进行归类,分开维护。
说明:大而全的常量类,非得使用查找功能才能定位到修改的常量,不利于理解和维护。
正例:缓存相关常量放在类CacheConts下;系统配置相关常量放在类ConfigConsts下。
三 OOP规约
1、避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可。
2、所有的覆写方法,必须加@Override注解
说明:getObject()与get0bject()的问题。一个是字母O,一个是数字0,加@Override可以准确判断是否覆盖成功。另外,如果在抽象类中方法签名进行修改,其实现类马上编译报错。
3、相同参数类型,相同业务含义,才可以使用java的可变参数,避免使用Object。
说明:可变参数必须放置在桉树列表的最后。(提倡大家尽量不用可变参数编程)
4、Object的equeal方法很容易抛空指针异常,应使用常量或确定有值的对象来调用。
正例:“Test”.equal(test);
反例:test.equal("Test");
5、所有的相同类型的包装类对象之间值的比较,全部使用equals方法比较。
说明:对于Integer i=? 在-128到127范围内的赋值,Integer对象是在IntegerCache.cache产生,会复用已有对象,这个区间内的Integer值可以直接使用==判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这个坑比较大,建议用equals比较。
6、关于基本数据类型和包装数据类型的使用标准
1、所有的POJO类属性必须使用包装数据类型
2、RPC方法的返回值和参数必须使用包装数据类型
3、所有局部变量使用基本数据类型(参考)。
说明:POJO类属性没有初始值是提醒使用者在需要使用时,必须自己显示的进行赋值,任何NPE(空指针异常)问题,或者入库检查,都由使用者保证。
7、定义DO/DTO/VO等POJO类时,不要设定任何初始值。
8、构造方法里面禁止加入任何业务逻辑,如果有初始化的逻辑,请昂在init方法中。
9、POJO类必须写toString方法。
说明:在方法执行抛出异常时,可以直接调用toString()方法进行打印其属性,便于排查问题。
10、循环体内,字符串的拼接使用StringBuff的append方法进行扩展。
11、慎用Object的clone方法来拷贝对象。
说明:colne方法默认是浅拷贝,浅拷贝:引用类型的属性,修改复制后的对象,则原对象也会改变。如果想实现深拷贝要重写clone方法实现属性对象的拷贝。
12、严控域,类成员与方法访问控制从严;
四集合处理
1、ArrayList的subList结果不可强转成ArrayList,否则会抛出ClassCasrException异常;
说明:subList返回的是ArrayList的内部类SubList,并不是ArrayList,而是ArrayList的一个视图,对于SubList子列表的所有操作最终都会反映到原列表上。
2、在subList场景中,高度注意对原集合元素个数的修改,会导致子列表的遍历、增加、删除均会产生异常。
3、使用集合转数组的方法,必须使用集合的toArray(T[] array),传入的是类型完全一样的数组,大小就是list.size();
说明:使用toArray带参方法,入参分配的数组空间不够大时,toArra方法内部将重新分配内存空间,并返回新数组地址:如果数组元素大于实际所需,下表[list.size()]的数组元素将被置为null,其他数组元素保持原值,因此最好将方法入参数组大小定为与集合元素个数一致。直接使用toArray无参方法存在问题,此方法返回值只能Object[]类,若强转其他的类型数组将出现类转换异常。
4、使用工具类Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的add/remove/clear方法会抛出异常。
说明:asList的返回对象是一个Arrays内部类,并没有实现集合的修改方法。Arrays.asList体现的是适配器模式,只是转换接口,后台的数据仍是数组。
5、不要在foreach循环里进行元素的remove/add操作。remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁。
6、集合初始化时,指定集合初始化大小。
说明:HashMap使用HashMap(int initialCapacity)初始化。initialCapacity=(需要存储的元素个数/负载因子)+1。注意:负载因子(loaderfactor)默认为0.75,如果暂时无法确定初始值大小,请设置为16(即默认值)。
反例:HashMap需要放置1024个元素,由于没有设置初始化大小,随着元素不断增加,容量7扩容,resize需要重建hash表,严重影响性能。
7、使用entrySet遍历Map类集合KV,而不是keySet方式进行遍历。
说明:keySet其实是遍历了2次,一次是转为Iterator对象,另一次是从hashMap中取出key所对应的Value。而entrySet只是遍历一次就把key和Value都放到了entry中,效率更高。如果是JDK8,使用Map.foreach方法。
8、高度注意Map集合K/V能不能存储null值的情况,如下:
集合类 | Key | Value | Super | 说明 |
Hashtable | 不允许为null | 不允许为null | Dictionary | 线程安全 |
ConcurrentHashMap | 不允许为null | 不允许为null | AbstractMap | 锁分段技术(JDK8:CAS) |
TreeMap | 不允许为null | 允许为null | AbstractMap | 线程不安全 |
HashMap | 允许为null | 允许为null | AbstractMap | 线程不安全 |
五控制语句
1、表达异常的分支时,少用if-else方式。
说明:如果非得用if()…else if()…else…方式表达逻辑,避免后续代码维护困难,请勿超过3层。超过三层的的逻辑可以用卫语句、策略模式、状态模式等来实现。
2、除常用方法外,不要在条件判断中执行其他复杂的语句,将复杂的逻辑的判断结果赋值给一个有意义的布尔值变量
3、循环体中的语句要考量性能,以下操作尽量移至循环体外处理,如定义对象、变量、获取数据库链接,进行不必要的try-catch操作
4、下列情况,需要进行参数校验:
1)调用频次低的方法
2)执行时间开销很大的方法,此情形中,参数校验时间几乎可以忽略不计,但如果因为参数错误导致中间执行回退,或者错误,那就得不偿失。
3)需要极高稳定性和可用性的方法。
4)对外提供的开放接口,不管是RPC/API/HTTP接口
5)敏感权限入口
六注释规约
1、类、类属性、类方法的注释必须使用Javadoc规范。
七其他
1、不要在视图模板中加入任何复杂的逻辑
说明:根据MVC理论,视图的职责是展示,不要抢模型和控制器的活;
2、任何数据结构的构造或初始化,都应指定大小,避免数据结构无限次增长吃光内存。