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- ArtifactID 格式:产品线名-模块名。语义不重复不遗漏,先到中央仓库去查证一下。
正例:dubbo-client / fastjson-api / jstorm-tool
- ArtifactID 格式:产品线名-模块名。语义不重复不遗漏,先到中央仓库去查证一下。
- Version:详细规定参考下方
- 禁止在子项目的 pom 依赖中出现相同的 GroupId,相同的 ArtifactId,但是不同的
Version。