对阿里巴巴java开发手册中所有的强制内容进行了整理
一、编程规约
1、命名风格
代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。
代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。
类名使用 UpperCamelCase 风格,必须遵从驼峰形式,但以下情形例外: DO / BO / DTO / VO / AO
方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格,必须遵从驼峰形式。
常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。
抽象类命名使用 Abstract 或 Base 开头 ; 异常类命名使用 Exception 结尾 ; 测试类命名以它要测试的类的名称开始,以 Test 结尾。
中括号是数组类型的一部分,数组定义如下: String[] args;
POJO 类中布尔类型的变量,都不要加 is ,否则部分框架解析会引起序列化错误。
包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用单数形式,但是类名如果有复数含义,类名可以使用复数形式。
杜绝完全不规范的缩写,避免望文不知义。
2、常量定义
不允许任何魔法值 ( 即未经定义的常量 ) 直接出现在代码中。
long 或者 Long 初始赋值时,使用大写的 L ,不能是小写的 l ,小写容易跟数字 1 混淆,造成误解。
3、代码格式
大括号的使用约定。如果是大括号内为空,则简洁地写成{}即可,不需要换行 ; 如果是非空代码块则:
1 ) 左大括号前不换行。
2 ) 左大括号后换行。
3 ) 右大括号前换行。
4 ) 右大括号后还有 else 等代码则不换行 ; 表示终止的右大括号后必须换行。
左小括号和字符之间不出现空格 ; 同样,右小括号和字符之间也不出现空格。详见第 5 条下方正例提示。
if / for / while / switch / do 等保留字与括号之间都必须加空格。
任何二目、三目运算符的左右两边都需要加一个空格。
采用 4 个空格缩进,禁止使用 tab 字符
注释的双斜线与注释内容之间有且仅有一个空格。
单行字符数限制不超过 120 个,超出需要换行,换行时遵循如下原则:
1) 第二行相对第一行缩进 4 个空格,从第三行开始,不再继续缩进,参考示例。
2 ) 运算符与下文一起换行。
3 ) 方法调用的点符号与下文一起换行。
4 ) 方法调用时,多个参数,需要换行时,在逗号后进行。
5 ) 在括号前不要换行,见反例。
方法参数在定义和传入时,多个参数逗号后边必须加空格。
IDE 的 text file encoding 设置为 UTF -8 ; IDE 中文件的换行符使用 Unix 格式,不要使用 Windows 格式。
1、OOP规约
避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可。
所有的覆写方法,必须加@ Override 注解。
相同参数类型,相同业务含义,才可以使用 Java 的可变参数,避免使用 Object 。
外部正在调用或者二方库依赖的接口,不允许修改方法签名,避免对接口调用方产生影响。接口过时必须加@ Deprecated 注解,并清晰地说明采用的新接口或者新服务是什么。
不能使用过时的类或方法。
Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用equals 。
所有的相同类型的包装类对象之间值的比较,全部使用 equals 方法比较。
关于基本数据类型与包装数据类型的使用标准如下:
1 ) 【强制】所有的 POJO 类属性必须使用包装数据类型。
2 ) 【强制】 RPC 方法的返回值和参数必须使用包装数据类型。
定义 DO / DTO / VO 等 POJO 类时,不要设定任何属性默认值。
序列化类新增属性时,请不要修改 serialVersionUID 字段,避免反序列失败 ; 如果完全不兼容升级,避免反序列化混乱,那么请修改 serialVersionUID 值。
构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在 init 方法中。
POJO 类必须写 toString 方法。使用 IDE 的中工具: source > generate toString时,如果继承了另一个 POJO 类,注意在前面加一下 super . toString 。
5、集合处理
关于 hashCode 和 equals 的处理,遵循如下规则:
1) 只要重写 equals ,就必须重写 hashCode 。
2) 因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的
对象必须重写这两个方法。
3) 如果自定义对象做为 Map 的键,那么必须重写 hashCode 和 equals 。
ArrayList 的 subList 结果不可强转成 ArrayList ,否则会抛出 ClassCastException异常,即 java . util . RandomAccessSubList cannot be cast to java . util . ArrayList .
在 subList 场景中,高度注意对原集合元素个数的修改,会导致子列表的遍历、增加、删除均会产生 ConcurrentModificationException 异常
使用集合转数组的方法,必须使用集合的 toArray(T[] array) ,传入的是类型完全一样的数组,大小就是 list . size()
使用工具类 Arrays . asList() 把数组转换成集合时,不能使用其修改集合相关的方法,它的 add / remove / clear 方法会抛出 UnsupportedOperationException 异常
泛型通配符 extends T >来接收返回的数据,此写法的泛型集合不能使用 add 方法,而 super T> 不能使用 get 方法,做为接口调用赋值时易出错。
不要在 foreach 循环里进行元素的 remove / add 操作。 remove 元素请使用 Iterator方式,如果并发操作,需要对 Iterator 对象加锁
在 JDK 7 版本及以上, Comparator 要满足如下三个条件,不然 Arrays . sort ,Collections . sort 会报 IllegalArgumentException 异常。
6、并发处理
获取单例对象需要保证线程安全,其中的方法也要保证线程安全。
创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。
线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
SimpleDateFormat 是线程不安全的类,一般不要定义为 static 变量,如果定义为static ,必须加锁,或者使用 DateUtils 工具类。
高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁 ; 能锁区块,就不要锁整个方法体 ; 能用对象锁,就不要用类锁。
对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造成死锁。
并发修改同一记录时,避免更新丢失,需要加锁。要么在应用层加锁,要么在缓存加锁,要么在数据库层使用乐观锁,使用 version 作为更新依据。
多线程并行处理定时任务时, Timer 运行多个 TimeTask 时,只要其中之一没有捕获抛出的异常,其它任务便会自动终止运行,使用 ScheduledExecutorService 则没有这个问题。
7、控制语句
在一个 switch 块内,每个 case 要么通过 break / return 等来终止,要么注释说明程序将继续执行到哪一个 case 为止 ; 在一个 switch 块内,都必须包含一个 default 语句并且放在最后,即使它什么代码也没有。
在 if / else / for / while / do 语句中必须使用大括号。即使只有一行代码,避免采用单行的编码方式: if (condition) statements
8、注释规约
类、类属性、类方法的注释必须使用 Javadoc 规范,使用/*内容/格式,不得使用// xxx 方式。
所有的抽象方法 ( 包括接口中的方法 ) 必须要用 Javadoc 注释、除了返回值、参数、异常说明外,还必须指出该方法做什么事情,实现什么功能。
所有的类都必须添加创建者和创建日期。
方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释使用/* */注释,注意与代码对齐。
所有的枚举类型字段必须要有注释,说明每个数据项的用途。
9、其他
在使用正则表达式时,利用好其预编译功能,可以有效加快正则匹配速度。
velocity 调用 POJO 类的属性时,建议直接使用属性名取值即可,模板引擎会自动按规范调用 POJO 的 getXxx() ,如果是 boolean 基本数据类型变量 (boolean 命名不需要加 is前缀 ) ,会自动调用 isXxx() 方法。
后台输送给页面的变量必须加 $!{var} ——中间的感叹号。
注意 Math . random() 这个方法返回是 double 类型,注意取值的范围 0≤ x <1 ( 能够取到零值,注意除零异常 ) ,如果想获取整数类型的随机数,不要将 x 放大 10 的若干倍然后取整,直接使用 Random 对象的 nextInt 或者 nextLong 方法。
获取当前毫秒数 System . currentTimeMillis(); 而不是 new Date() . getTime();
二、异常日志
1、异常处理
Java 类库中定义的一类 RuntimeException 可以通过预先检查进行规避,而不应该通过 catch 来处理,比如: IndexOutOfBoundsException , NullPointerException 等等。
异常不要用来做流程控制,条件控制,因为异常的处理效率比条件分支低。
对大段代码进行 try - catch ,这是不负责任的表现。 catch 时请分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码。对于非稳定代码的 catch 尽可能进行区分异常类型,再做对应的异常处理。
捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的内容。
有 try 块放到了事务代码中, catch 异常后,如果需要回滚事务,一定要注意手动回滚事务。
finally 块必须对资源对象、流对象进行关闭,有异常也要做 try - catch 。
不能在 finally 块中使用 return , finally 块中的 return 返回后方法结束执行,不会再执行 try 块中的 return 语句。
捕获异常与抛异常,必须是完全匹配,或者捕获异常是抛异常的父类。
2、日志规约
应用中不可直接使用日志系统 (Log 4 j 、 Logback) 中的 API ,而应依赖使用日志框架SLF 4 J 中的 API ,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。
日志文件推荐至少保存 15 天,因为有些异常具备以“周”为频次发生的特点。
应用中的扩展日志 ( 如打点、临时监控、访问日志等 ) 命名方式:
appName _ logType _ logName . log 。 logType :日志类型,推荐分类有
stats / desc / monitor / visit 等 ;logName :日志描述。这种命名的好处:通过文件名就可知道日志文件属于什么应用,什么类型,什么目的,也有利于归类查找。
对 trace / debug / info 级别的日志输出,必须使用条件输出形式或者使用占位符的方式。
避免重复打印日志,浪费磁盘空间,务必在 log 4 j . xml 中设置 additivity = false 。
异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么通过关键字 throws 往上抛出。
三、单元测试
好的单元测试必须遵守 AIR 原则。
单元测试应该是全自动执行的,并且非交互式的。测试框架通常是定期执行的,执行过程必须完全自动化才有意义。输出结果需要人工检查的测试不是一个好的单元测试。单元测试中不准使用 System.out 来进行人肉验证,必须使用 assert 来验证。
保持单元测试的独立性。为了保证单元测试稳定可靠且便于维护,单元测试用例之间决不能互相调用,也不能依赖执行的先后次序。
单元测试是可以重复执行的,不能受到外界环境的影响。
对于单元测试,要保证测试粒度足够小,有助于精确定位问题。单测粒度至多是类级别,一般是方法级别。
核心业务、核心应用、核心模块的增量代码确保单元测试通过。
单元测试代码必须写在如下工程目录: src/test/java ,不允许写在业务代码目录下。
四、安全规约
隶属于用户个人的页面或者功能必须进行权限控制校验。
用户敏感数据禁止直接展示,必须对展示数据进行脱敏。
用户输入的 SQL 参数严格使用参数绑定或者 METADATA 字段值限定,防止 SQL 注入,禁止字符串拼接 SQL 访问数据库。
用户请求传入的任何参数必须做有效性验证。
禁止向 HTML 页面输出未经安全过滤或未正确转义的用户数据。
表单、 AJAX 提交必须执行 CSRF 安全过滤。
在使用平台资源,譬如短信、邮件、电话、下单、支付,必须实现正确的防重放限制,如数量限制、疲劳度控制、验证码校验,避免被滥刷、资损。
五、MySQL数据库
1、建表规约
表达是与否概念的字段,必须使用 is _ xxx 的方式命名,数据类型是 unsigned tinyint( 1 表示是,0 表示否 ) 。
表名、字段名必须使用小写字母或数字 , 禁止出现数字开头,禁止两个下划线中间只出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。
表名不使用复数名词。
禁用保留字,如 desc 、 range 、 match 、 delayed 等,请参考 MySQL 官方保留字。
主键索引名为 pk_ 字段名;唯一索引名为 uk _字段名 ; 普通索引名则为 idx _字段名。
小数类型为 decimal ,禁止使用 float 和 double 。
如果存储的字符串长度几乎相等,使用 char 定长字符串类型。
varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长度大于此值,定义字段类型为 text ,独立出来一张表,用主键来对应,避免影响其它字段索引效率。
表必备三字段: id , gmt _ create , gmt _ modified 。
2、索引规约
业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引。
超过三个表禁止 join 。需要 join 的字段,数据类型必须绝对一致 ; 多表关联查询时,保证被关联的字段需要有索引。
在 varchar 字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度即可。
页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。
3、SQL语句
不要使用 count( 列名 ) 或 count( 常量 ) 来替代 count( * ) , count( * ) 是 SQL 92 定义的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。
count(distinct col) 计算该列除 NULL 之外的不重复行数,注意 count(distinctcol 1, col 2 ) 如果其中一列全为 NULL ,那么即使另一列有不同的值,也返回为 0。
当某一列的值全是 NULL 时, count(col) 的返回结果为 0,但 sum(col) 的返回结果为NULL ,因此使用 sum() 时需注意 NPE 问题。
使用 ISNULL() 来判断是否为 NULL 值。
在代码中写分页查询逻辑时,若 count 为 0 应直接返回,避免执行后面的分页语句。
不得使用外键与级联,一切外键概念必须在应用层解决。
禁止使用存储过程,存储过程难以调试和扩展,更没有移植性。
数据订正时,删除和修改记录时,要先 select ,避免出现误删除,确认无误才能执行更新语句。
4、ORM映射
在表查询中,一律不要使用 * 作为查询的字段列表,需要哪些字段必须明确写明。
POJO 类的 布尔 属性不能加 is ,而数据库字段必须加 is _,要求在 resultMap 中进行字段与属性之间的映射。
不要用 resultClass 当返回参数,即使所有类属性名与数据库字段一一对应,也需要定义 ; 反过来,每一个表也必然有一个与之对应。
sql. xml 配置参数使用:#{},# param # 不要使用${} 此种方式容易出现 SQL 注入。
iBATIS 自带的 queryForList(String statementName , int start , int size) 不推荐使用。
不允许直接拿 HashMap 与 Hashtable 作为查询结果集的输出。
更新数据表记录时,必须同时更新记录对应的 gmt _ modified 字段值为当前时间。
六、工程结构
1、二方库依赖
定义 GAV 遵从以下规则:
1 ) GroupID 格式: com .{公司/ BU }.业务线. [ 子业务线 ] ,最多 4 级。
说明:{公司/ BU } 例如: alibaba / taobao / tmall / aliexpress 等 BU 一级 ; 子业务线可选。
正例: com . taobao . jstorm 或 com.alibaba.dubbo.register
2 ) ArtifactID 格式:产品线名-模块名。语义不重复不遗漏,先到中央仓库去查证一下。
正例: dubbo - client / fastjson - api / jstorm - tool
3 ) Version :详细规定参考下方。
二方库版本号命名方式:主版本号.次版本号.修订号
1 ) 主版本号 主版本号:产品方向改变,或者大规模 API 不兼容,或者架构不兼容升级。
2 ) 次版本号 次版本号:保持相对兼容性,增加主要功能特性,影响范围极小的 API 不兼容修改。
3 ) 修订号 修订号:保持完全兼容性,修复 BUG 、新增次要功能特性等。
线上应用不要依赖 SNAPSHOT 版本 ( 安全包除外 )。
二方库的新增或升级,保持除功能点之外的其它 jar 包仲裁结果不变。如果有改变,必须明确评估和验证,建议进行 dependency : resolve 前后信息比对,如果仲裁结果完全不一致,那么通过 dependency : tree 命令,找出差异点,进行< excludes >排除 jar 包。
二方库里可以定义枚举类型,参数可以使用枚举类型,但是接口返回值不允许使用枚举类型或者包含枚举类型的 POJO 对象。
依赖于一个二方库群时,必须定义一个统一的版本变量,避免版本号不一致。
禁止在子项目的 pom 依赖中出现相同的 GroupId ,相同的 ArtifactId ,但是不同的Version 。