1. 抽象类命名使用 Abstratc开头。
2. 阿里强制规定不允许任何魔法值(未经定义的常量)直接出现在代码中。魔法值会让代码的可读性大大降低,而且如果同样的数值多次出现时,容易出现不清楚这些数值是否代表同样的
含义。另一方面,如果本来应该使用相同的数值,一旦用错,也难以发现。因此可以采用以下两点,极力避免使用魔法数值。
3. 阿里推荐如果变量值仅在一个范围内变化,且带有名称之外的延伸属性,定义为枚举类。对于固定并且编译时对象,如 Status、Type 等,应该采用 enum 而非自定义常量实现,enum 的
好处是类型更清楚,不会再编译时混淆。这是一个建议性的试用推荐,枚举可以让开发者在 IDE 下使用更方便,也更安全。另外就是枚举类型是一种具有特殊约束的类类型,这些约束的
存在使得枚举类本身更加简洁、安全、便捷。
4. 阿里强制规定如果是大括号为空,则简洁地写成{}即可,不需要换行
5. 阿里强制规定单行字符数限制不超过 120 个,超出需要换行,换行时遵循如下原则:
1).第二行相对第一行缩进 4 个空格,从第三行开始,不再继续缩进,参考示例。
2).运算符与下文一起换行。
3).方法调用的点符号与下文一起换行。
4).方法调用时,多个参数,需要换行时,在逗号后进行。
5).在括号前不要换行
6. 阿里强制规定代码中避免通过一个类的对象引用访问此类的静态变量或静态方法,暂时无谓增加编译器解析成本,直接用类名来访问即可。
为什么需要这样做呢?因为被 static 修饰过的变量或者方法都是随着类的初始化产生的,在堆内存中有一块专门的区域用来存放,后续直接用类名访问即可,避免编译成本的增加和实例
对象存放空间的浪费。
StackOverflow 上也有人提出了相同的疑问,网友较为精辟的回复是"这是由于生命周期决定的,静态方法或者静态变量不是以实例为基准的,而是以类为基准,所以直接用类访问,否则
违背了设计初衷"。那为什么还保留了实例的访问方式呢?可能是因为允许应用方无污染修改吧。
7. 阿里强制规定相同参数类型、相同业务类型,才可以使用 Java 的可变参数,避免使用 Object,并且要求可变参数必须放置在参数列表的最后(提倡同学们尽量不用可变参数编程)。
拥有可变参数的方法可以被重载,在被调用时,如果能匹配到参数定长的方法则优先调用参数定长的方法。
8.单例模式:
1).双检查锁机制(Double Check Locking),这里在声明变量时使用了 volatile 关键字来保证其线程间的可见性;在同步代码块中使用二次检查,以保证其不被重复实例化。
集合其二者,这种实现方式既保证了其高效性,也保证了其线程安全性。
2).懒汉式:
9. 阿里强制规定在一个 switch 块内,每个 case 要么通过 break/return 等来终止,要么注释说明程序将继续执行到哪一个 case 为止;在一个 switch 块内,都必须包含一个 default
语句并且放在最后,即使它什么代码也没有。语法上来说,default 语句中的 break 是多余的,但是如果后续添加额外的 case,可以避免找不到匹配 case 项的错误。
10. 阿里强制规定使用集合转数组的方法,必须使用集合的 toArray(T[] arrays),传入的是类型完全一样的数组,大小就是 list.size()。使用 toArray 带参方法,入参分配的数组空间不够大时,
toArray 方法内部将重新分配内存空间,并返回新数组地址;如果数组元素大于实际所需,下标为[list.size()]的数组元素将被置为 null,其它数组元素保持原值,因此最好将方法入参数组大小定义与集合元素个数一致。
String[] array = new String[list.size()];
array = list.toArray(array);
11. 阿里强制要求方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释使用/**/注释,注意与代码对照。
百度规定方法注释采用标准的 Javadoc 注释规范,注释中必须提供方法说明、参数说明及返回值和异常说明。腾讯规定采用 JavaDoc 文档注释,在方法定义之前应该对其进行注释,
包括方法的描述、输入、输出以及返回值说明、抛出异常说明、参考链接等。
12. 不带参数的 toArray()构造一个 Object 数组,然后进行数据拷贝
13. 阿里推荐任何数据结构的构造或初始化,都应指定大小,避免数据结构暂时无限增长吃光内存。
首先明确一点,阿里这里指的大小具体是指数据结构的最大长度。大部分 Java 集合类在构造时指定的大小都是初始尺寸(initial Capacity),而不是尺寸上限(Capacity),只有几
种队列除外,例如 ArrayBlockingQueue、LinkedBlockingQueue,它们在构造时可以指定队列的最大长度。阿里推荐的目的是为了合理规划内存,避免出现 OOM(Out of Memory)异常。
14. 阿里强制规定 Java 类库中的 RuntimeException 可以通过预先检查进行规避,而不应该通过 catch 来处理,例如 IndexOutOfBoundsException、NullPointerException 等。
RuntimeException,也被称为运行时异常,通常是由于代码中的 bug 引起的,正确的处理方式是去检查代码,通过添加数据长度判断,判断对象是否为空等方法区规避,而不是靠捕获来规避这种异常。
15. 阿里强制规定有 try 块放到了事务代码中,catch 异常后,如果需要回滚事务,一定要注意手动回滚事务。
try catch 代码块中对异常的处理,可能会遗漏事务的一致性,当事务控制不使用其他框架管理时,事务需要手动回滚。实际使用如果引入第三方的框架对事务进行管理,比如 Spring,则根据第三方
框架的实际实现情况,确定是否有必要手动回滚。当第三方事务管理框架本身就会对于异常进行抛出时需要做事务回滚。例如 Spring 在@Transactional 的 annotation 注解下,会默认开启运行时异常事务回滚。
16. 阿里强制规定应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架 SLF4J 中的 API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。
17. 阿里强制规定日志文件至少保存 15 天,因为有些异常具备以"周"为频次发生的特点。
日志保留时间推荐 15 天以上,但是保留时间也不宜过长,一般不超过 21 天,否则造成硬盘空间的浪费。对于一些长周期性执行的逻辑,可以根据实际情况调整该保存时间,同时也
需要保证日志能够监控到关键的应用。
对于长周期执行的逻辑,可以使用特定的 appender,并使用不同的日志清理规则,如时间、大小等。如一月执行一次的定时任务,可以将日志输出到新的日志文件,然后通过大小限定
的规则进行清理,并不一定要使用时间清理的逻辑。
18. 阿里强制要求对于隶属于用户个人的页面或者功能必须进行权限控制校验。
涉及到对于数据的增删改查,必须有权限的控制和校验,要有一个黑白名单的控制,不能依赖于前台页面的简单控制,后台要有对于完整的权限控制的实现。这样就能尽可能地防治数据的错误修改。
19. 阿里强制要求用户请求传入的任何参数必须做有效校验。
20.单元测试:
1). 单元测试应该自动执行:阿里强制单元测试应该是全自动执行的,并且非交互式的。测试框架通常是定期执行的,执行过程必须完全自动化才有意义。输出结果需要人工检查的测试
不是一个号的单元测试。单元测试中不准使用 System.out 来进行人肉验证,必须使用 assert 来验证。
2). 单元测试应该是独立的:阿里强制保持单元测试的独立性。为了保证单元测试稳定可靠且便于维护,单元测试用例之间决不能互相调用,也不能依赖执行的先后次序。反例:method2
需要依赖 method1 的执行,将执行结果作为 method2 的输入。
编写单元测试时遇到的这类依赖可以使用 mock 来模拟输入和期望的返回,这样所以来的方法内部逻辑的变更就不会影响到外部的实现。
3).阿里推荐编写单元测试代码遵守 BCDE 原则,以保证被测试模块的交付质量。
a). B(Border):确保参数边界值均被覆盖。例如:对于数字,测试负数、0、正数、最小值、最大值、NaN(非数字)、无穷大值等。对于字符串,测试空字符串、单字符、
非 ASCII 字符串、多字节字符串等。对于集合类型,测试空、第一个元素、最后一个元素等。对于日期,测试 1 月 1 日、2 月 29 日、12 月 31 日等。被测试的类本身
也会暗示一些特定情况下的边界值。对于边界情况的测试一定要详尽。
b). C(Connect):确保输入和输出的正确关联性。例如,测试某个时间判断的方法 boolean inTimeZone(Long timeStamp),该方法根据输入的时间戳判断该事件是否存在于某个
时间段内,返回 boolean 类型。如果测试输入的测试数据为 Long 类型的时间戳,对于输出的判断应该是对于 boolean 类型的处理。如果测试输入的测试数据为非 Long 类型
数据,对于输出的判断应该是报错信息是否正确。
c).D(Design):任务程序的开发包括单元测试都应该遵循设计文档。
d).E(Error):单元测试包括对各种方法的异常测试,测试程序对异常的响应能力。
21.《单元测试之道(Java 版)》这本书里面提到了关于边界测试的 CORRECT 原则:
1).一致性(Conformance):值是否符合预期格式(正常的数据),列出所有可能不一致的数据,进行验证。
2).有序性(Ordering):传入的参数的顺序不同的结果是否正确,对排序算法会产生影响,或者是对类的属性赋值顺序不同会不会产生错误。
3).区间性(Range):参数的取值范围是否在某个合理的区间范围内。
4).引用/耦合性(Reference):程序依赖外部的一些条件是否已满足。前置条件:系统必须处于什么状态下,该方法才能运行。后置条件,你的方法将会保证哪些状态发生改变。
5).存在性(Existence):参数是否真的存在,引用为 Null,String 为空,数值为 0 或者物理介质不存在时,程序是否能正常运行。
6).基数性(Cardinality):考虑以"0-1-N 原则",当数值分别为 0、1、N 时,可能出现的结果,其中 N 为最大值。
7).时间性(Time):相对时间指的是函数执行的依赖顺序,绝对时间指的是超时问题、并发问题。
22. 阿里强制要求如果遇到需要表达是与否的概念时,必须使用 is_xxx 的方法命令,数据类型是 unsigned tinyint,1 表示是,0 表示否。
说明:任务字段如果为非负数,必须是 unsigned。
命名使用 is_xxx 第一个好处是比较清晰的,第二个好处是使用者根据命名就可以知道这个字段的取值范围,也方便做参数验证。类型使用 unsigned 的好处是如果只存整数,
unsigned 类型有更大的取值范围,可以节约磁盘和内存使用。
23. 对于表的名字,mysql 社区有自己推荐的命名规范:
1).表包含多个英文单词时,需要用下划线进行单词分割,这一点类似于 Java 类名的命名规范,例如 master_schedule、security_user_permission;
2).由于 InnoDB 存储引擎本身是针对操作系统的可插拔设计的,所以原则上所有的表名组成全部由小写字母组成;
3).不允许出现空格,需要分割一律采用下划线;
4).名字不允许出现数字,仅包含英文字母;
5).名字需要总长度少于 64 个字符。
24. 数据类型精度考量:阿里强制要求存放小数时使用 decimal,禁止使用 float 和 double。
说明:float 和 double 在存储的时候,存在精度损失的问题,很可能在值的比较时,得到不正确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。
实际上,所有涉及到数据存储的类型定义,都会涉及数据精度损失问题。Java 的数据类型也存在 float 和 double 精度损失情况,阿里没有指出这条规约,就全文来说,这是一个比较严重的规约缺失。
25. 采用 BigDecimal 有一个缺点,就是使用过程中没有原始数据这么方便,效率也不高。如果采用 int 方式,最好不要在有小数点的场景下使用,可以在 100、10 这样业务场景下选择使用。
26. 阿里强制要求如果存储的字符串长度几乎相等,使用 Char 定长字符串类型。
27. 服务间依赖关系:阿里推荐默认上层依赖于下层,箭头关系表示可直接依赖,如:开放接口层可以依赖于 Web 层,也可以直接依赖于 Service 层。
28. 阿里推荐高并发服务器建议调小 TCP 协议的 time_wait 超时时间。
说明:操作系统默认 240 秒后才会关闭处于 time_wait 状态的连接,在高并发访问下,服务器端会因为处于 time_wait 的连接数太多,可能无法建立新的连接,所以需要在服务器上调小此等待值。
正例:在 Linux 服务器上通过变更/etc/sysctl.conf 文件去修改该缺省值(秒):net.ipv4.tcp_fin_timeout=30
参见:https://www.ibm.com/developerworks/cn/java/deconding-code-specification-part-1/index.html
https://www.ibm.com/developerworks/cn/java/deconding-code-specification-part-2/index.html?ca=drs-