alibaba开发手册
11.19
-
强制: 方法参数在定义和传入时,多个参数逗号后边必须加空格。
-
IDE 的 text file encoding 设置为 UTF-8; IDE 中文件的换行符使用 Unix 格式,不
要使用 Windows 格式。 -
避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析
成本,直接用类名来访问即可。 -
Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用
equals。 -
所有整型包装类对象之间值的比较,全部使用 equals 方法比较
-
浮点数之间的等值判断,基本数据类型不能用==来比较,包装数据类型不能用
equals 来判断 -
数据库字段的 bigint 必须与类属性的 Long 类型相对应。
-
为了防止精度损失,禁止使用构造方法 BigDecimal(double)的方式把 double 值转
化为 BigDecimal 对象 -
构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在 init 方法中。
-
循环体内,字符串的连接方式,使用 StringBuilder 的 append 方法进行扩展。
-
final 可以声明类、成员变量、方法、以及本地变量,下列情况使用 final 关键字:
1) 不允许被继承的类,如:String 类。
2) 不允许修改引用的域对象。
3) 不允许被覆写的方法,如:POJO 类的 setter 方法。
4) 不允许运行过程中重新赋值的局部变量。
5) 避免上下文重复使用一个变量,使用 final 可以强制重新定义一个变量,方便更好地进行重构。
11.20
1. 使用集合转数组的方法,必须使用集合的 toArray(T[] array),传入的是类型完全一
致、长度为 0 的空数组。
反例:直接使用 toArray 无参方法存在问题,此方法返回值只能是 Object[]类,若强转其它类型数组将出
现 ClassCastException 错误。
2. List<String> list = new ArrayList<>(2);
list.add("guan");
list.add("bao");
String[] array = list.toArray(new String[0]);
说明:使用 toArray 带参方法,数组空间大小的 length:
1) 等于 0,动态创建与 size 相同的数组,性能最好。
2) 大于 0 但小于 size,重新创建大小等于 size 的数组,增加 GC 负担。
3) 等于 size,在高并发情况下,数组创建完成之后,size 正在变大的情况下,负面影响与上相同。
4) 大于 size,空间浪费,且在 size 处插入 null 值,存在 NPE 隐患。
3. 不要在 foreach 循环里进行元素的 remove/add 操作。 remove 元素请使用
Iterator 方式,如果并发操作,需要对 Iterator 对象加锁。 正例:
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if (删除元素的条件) {
Java 开发手册
13/44
iterator.remove();
}
}
反例:
for (String item : list) {
if ("1".equals(item)) {
list.remove(item);
}
}
4. ArrayList 是 order/unsort;HashMap 是 unorder/unsort;TreeSet 是 order/sort。
5. 【参考】 利用 Set 元素唯一的特性,可以快速对一个集合进行去重操作,避免使用 List 的
contains 方法进行遍历、对比、 去重操作。
11.21
并发处理
1. 对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会
造成死锁。
2. 并发修改同一记录时,避免更新丢失,需要加锁。 要么在应用层加锁,要么在缓存
加锁,要么在数据库层使用乐观锁,使用 version 作为更新依据。
说明:如果每次访问冲突概率小于 20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次数不得小于
3 次
控制语句
1. 在一个 switch 块内,每个 case 要么通过 continue/break/return 等来终止,要么
注释说明程序将继续执行到哪一个 case 为止;在一个 switch 块内,都必须包含一个
default 语句并且放在最后,即使它什么代码也没有。
说明:注意 break 是退出 switch 语句块,而 return 是退出方法体
2. 在高并发场景中,避免使用” 等于” 判断作为中断或退出的条件。
说明:如果并发控制没有处理好,容易产生等值判断被“击穿” 的情况,使用大于或小于的区间判断条件
来代替。
3. 【推荐】 表达异常的分支时,少用 if-else 方式,这种方式可以改写成:
if (condition) {
...
return obj;
}
// 接着写 else 的业务逻辑代码
4. ```java
package com.yamon.array;
public class SwitchString {
public static void main(String[] args) {
method(null);
}
public static void method(String param) {
switch (param) {
// 肯定不是进入这里
case "sth":
System.out.println("it's sth");
break;
// 也不是进入这里
case "null":
System.out.println("it's null");
break;
// 也不是进入这里
default:
System.out.println("default");
}
}
}
```
![image-20191121085237517](C:\Users\陈亚萌\AppData\Roaming\Typora\typora-user-images\image-20191121085237517.png)
5. 循环体中的语句要考量性能,以下操作尽量移至循环体外处理,如定义对象、变
量、获取数据库连接,进行不必要的 try-catch 操作(这个 try-catch 是否可以移至循环体
外)
11.22
1. 类、类属性、类方法的注释必须使用 Javadoc 规范,使用/**内容*/格式,不得使用
// xxx 方式
2. 所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释、除了返回值、参数、
异常说明外,还必须指出该方法做什么事情,实现什么功能
3. 代码修改的同时,注释也要进行相应的修改,尤其是参数、返回值、异常、核心逻
辑等的修改。
4. 错误,不能工作(FIXME):(标记人,标记时间,[预计处理时间])
在注释中用 FIXME 标记某代码是错误的,而且不能工作,需要及时纠正的情况。
5. 在使用正则表达式时,利用好其预编译功能,可以有效加快正则匹配速度。
6. 后台输送给页面的变量必须加$!{var}——中间的感叹号。
7. 注意 Math.random() 这个方法返回是 double 类型,注意取值的范围 0≤x<1(能够
取到零值,注意除零异常),如果想获取整数类型的随机数,不要将 x 放大 10 的若干倍然后
取整,直接使用 Random 对象的 nextInt 或者 nextLong 方法。
8. 获取当前毫秒数 System.currentTimeMillis(); 而不是 new Date().getTime();
说明:如果想获取更加精确的纳秒级时间值,使用 System.nanoTime()的方式。在 JDK8 中,针对统计时
间等场景,推荐使用 Instant 类。
9. 日期格式化时,传入 pattern 中表示年份统一使用小写的 y。
说明:日期格式化时,yyyy 表示当天所在的年,而大写的 YYYY 代表是 week in which year
(JDK7 之后引入的概念),意思是当天所在的周属于的年份,一周从周日开始,周六结束,
只要本周跨年,返回的 YYYY 就是下一年。另外需要注意:
⚫ 表示月份是大写的 M
⚫ 表示分钟则是小写的 m
⚫ 24 小时制的是大写的 H
⚫ 12 小时制的则是小写的 h
正例:表示日期和时间的格式如下所示:
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
10. 任何数据结构的构造或初始化,都应指定大小,避免数据结构无限增长吃光内存。
11. **及时清理不再使用的代码段或配置信息。**
说明:对于垃圾代码或过时配置,坚决清理干净,避免程序过度臃肿,代码冗余。
正例:****对于暂时被注释掉,后续可能恢复使用的代码片断,在注释代码上方,统一规定使用三个斜杠(///)**
来说明注释掉代码的理由。**
二、异常日志
1. 捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,
请将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理
解的内容。
2. 防止 NPE,是程序员的基本修养,注意 NPE 产生的场景:
1) 返回类型为基本数据类型,return 包装数据类型的对象时,自动拆箱有可能产生 NPE。
反例:public int f() { return Integer 对象}, 如果为 null,自动解箱抛 NPE。
2) 数据库的查询结果可能为 null。
3) 集合里的元素即使 isNotEmpty,取出的数据元素也可能为 null。
4) 远程调用返回对象时,一律要求进行空指针判断,防止 NPE。
5) 对于 Session 中获取的数据,建议进行 NPE 检查,避免空指针。
6) 级联调用 obj.getA().getB().getC();一连串调用,易产生 NPE。
正例:使用 JDK8 的 Optional 类来防止 NPE 问题。
三、日志规约
1. 应用中的扩展日志(如打点、临时监控、访问日志等)命名方式:
appName_logType_logName.log。 logType:日志类型,如 stats/monitor/access 等;logName:日志
描述。这种命名的好处:通过文件名就可知道日志文件属于什么应用,什么类型,什么目的,也有利于归
类查找。
2. 避免重复打印日志,浪费磁盘空间,务必在 log4j.xml 中设置 additivity=false。
四、单元测试
1. 好的单元测试必须遵守 AIR 原则。
说明:单元测试在线上运行时,感觉像空气(AIR)一样并不存在,但在测试质量的保障上,却是非常关
键的。好的单元测试宏观上来说,具有自动化、独立性、可重复执行的特点。
⚫ A:Automatic(自动化)
⚫ I:Independent(独立性)
⚫ R:Repeatable(可重复)
2. 单元测试应该是全自动执行的,并且非交互式的。测试用例通常是被定期执行的,
执行过程必须完全自动化才有意义。输出结果需要人工检查的测试不是一个好的单元测试。
单元测试中不准使用 System.out 来进行人肉验证,必须使用 assert 来验证。
11.25
1. 用户请求传入的任何参数必须做有效性验证。
说明:忽略参数校验可能导致:
⚫ page size 过大导致内存溢出
⚫ 恶意 order by 导致数据库慢查询
⚫ 任意重定向
⚫ SQL 注入
⚫ 反序列化注入
⚫ 正则输入源串拒绝服务 ReDoS
说明:Java 代码用正则来验证客户端的输入,有些正则写法验证普通用户输入没有问题,但是如果攻
击人员使用的是特殊构造的字符串来验证,有可能导致死循环的结果。
2. 发贴、评论、发送即时消息等用户生成内容的场景必须实现防刷、文 本内容违禁词过滤等风控策略。
数据库安全
1. 表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类 型是 unsigned
tinyint(1 表示是,0 表示否)
2. 表名、字段名必须使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间只出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。
3. 表名不使用复数名词。
4. 小数类型为 decimal,禁止使用 float 和 double
5. varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长度大于此值,定义字段类型为 text,独立
出来一张表,用主键来对应,避免影响其它字段索引效率
6. 表必备三字段:id, create_time, update_time
7. 表的命名最好是遵循“业务名称_表的作用” 。
11.27
工程结构
应用分层
- 定义 GAV 遵从以下规则:
- 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 - Version:详细规定参考下方
- 禁止在子项目的 pom 依赖中出现相同的 GroupId,相同的 ArtifactId,但是不同的
Version。
博客网站 https://yamon.top
个人网站 https://yamon.top/resume
GitHub网站 https://github.com/yamonc
欢迎前来访问