我爱编程

阿里巴巴 Java 开发手册总结

2018-08-04  本文已影响18人  流年划破容颜_cc55

参考原文地址

第1章:编程规约

这一章是对传统意义上的代码规范,包括变量命名、代码风格、控制语句、代码注释等基本的变成习惯,以及从高并发场景中提炼出来的集合处理技巧与并发多线程的注意事项。

1.1 命名风格

第一条:【强制】代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。

  • 反例:_name / $name / name_ / name$

尽管 $ 可以作为标识符使用,然而我们应该尽量避免对其使用。

package test;

public class User$VIP {
    public static void main(String[] args) {
        User user = new User();
        User.VIP vip = user.new VIP();
        vip.print();
    }
}

class User{
    class VIP{
        void print(){
            System.out.println("成员类");
        }
    }
}

仔细阅读以下,似乎并没有什么问题,代码也比较简单,但正在我们编译的时候,IDEA提示我们:

image.png

定义了重复的代码?归根到底,都是 $ 惹的祸!因为 $ 被编译器所使用,在源文件(.java 文件)编译成字节码(.class 文件)后,会称为顶层类型与嵌套类型之间的连接符。例如,如果存在一个顶层类 A,在其内声明了一个成员类 B,那么编译之后就会产生两个 class 文件,分别为 A.classA$B.class

就本程序来说,会生成 3 个 class 文件(如果可以编译的话),分别是 User$VIP.class(顶层类)、User.classUser$VIP.class(User 类的成员类,也就是类 VIP)。由于试图存在两个 User$VIP.class 所以才会报错!

第三至第六条:【强制】

正例:MAX_STOCK_COUNT / PRIZE_NUMBER_EVERYDAY
反例:MAX_COUNT / PRIZE_NUMBER

第八条:【强制】 POJO 类中布尔类型的变量都不要加 is 前缀,否则部分框架解析会引起序列化错误。

反例:定义为基本数据类型 Boolen isDeleted; 的属性,它的方法名称也是 isDeleted() ,RPC 框架在反向解析的时候,“误以为” 对应的属性名称是 deleted ,导致属性获取不到抛出异常。

第十二条:【推荐】 如果模块、类、方法使用了设计模式,应在命名时体现出具体模式

正例:
public class OrderFactory;
public class LoginProxy;
public class ResourceObserver;

第十三条:【推荐】 接口类中的方法和属性不要加任何修饰符号(public 也不要加),保持代码的间接性,并加上有效的 Javadoc 注释。尽量不要在接口里定义变量,如果一定要定义变量,必须是与接口方法相关的,并且是整个应用的基础变量。

正例:
接口方法签名: void commit();
接口基础变量: String COMPANY = "alibaba";
反例:
接口定义方法: public abstract void commit();

第十四条:接口和实现类的命名规则

正例: CacheServiceImpl 实现 CacheServcie 接口

正例: AbstractTranslator 实现 Translatable。

1.2 常量定义

第二条:【强制】 long 或者 Long 初始赋值时,使用大写的 L,不能是小写的 l。小写的 l 容易跟数字 1 混淆,造成误解。

第三条:【推荐】 不要使用一个常量类维护所有变量,要按常量功能进行归类,分开维护。

正例:缓存相关常量放在类 CacheConsts 下;系统配置相关常量放在 ConfigConsts 下。

1.3 代码格式

public static void main(String[] args){
    // 注释的双斜线与注释内容之间有且仅有一个空格
    // 缩进 4 个空格
    String say = "hello";
    // 运算符的左右必须有 1 个空格
    int flag = 0;
    // 关键字 if 与括号之间必须有 1 个空格,括号内的 f与左括号、
    // 0 与右括号之间不需要空格
    if (flag == 0) {
        System.out.println(say);
    }
    // 左大括号前加空格且不换行;左大括号后换行
    if (flag == 1) {
        System.out.println("world");
    // 右大括号前换行,右大括号后有 else,不用换行
    } else {
        System.out.println("ok");
    // 在右大括号后直接结束,则必须换行
    }
}

第八条:【强制】 方法参数在定义和传入时,多个参数逗号后边必须加空格。

正例:下例中实参的“one”,后边必须要有一个空格。
method("one", "two", "three");

1.4 OOP 规约

第二条:【强制】 所有的复写方法,必须加 @Override 注解。

image.png

第七条:【强制】 所有相同类型的包装类对象之间值得比较,全部使用 equals 方法

第十二条:【强制】 POJO 类必须写 toString 方法。在使用 IDE 中的工具 source>generate toString 时,如果继承了另一个 POJO 类,注意在前面加一下 super.toString。

1.5 集合处理

第七条:【强制】 不要在 foreach 循环里进行元素的 remove / add 操作。remove 元素请使用 Iterator 方式,如果并发操作,需要对 Iterator 对象加锁。

// 正例
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String item = iterator.next();
    if (删除元素的条件) {
        iterator.remove();
    }
}
// 反例
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
for (String item : list) {
    // 如果把 1 改为 2 再试一下看看是否相同
    if ("1".equals(item)) {
        list.remove(item);
    }
}

image.png

第十一条:【推荐】 高度注意 Map 类集合 K/V 能不能存储 null 值得情况

image.png

1.6 并发处理

第三条:【强制】 线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。

image.png

1.7 控制语句

第二条:【强制】 在 if / else / for / while / do 语句中,必须使用大括号。即使只有一行代码,也应该避免采用单行的编码方式:if (condition) statements;

第三条:【强制】 在高并发场景中,避免使用 “等于” 判断作为终端或退出的条件

反例:判断剩余奖品数量等于 0 时,终止发放奖品,但因为并发处理错误导致奖品数量瞬间变成了负数,这样的话,活动无法终止。

第四条:【推荐】 在表达异常的分支时,尽量少用 if-else 方式

// 正例:超过 3 层的 if-else 逻辑判断代码可以使用卫语句、策略模式
// 状态模式等来实现,其中卫语句实例如下:
public void today() {
    if (isBusy()) {
        System.out.println("change time,");
        return;
    }

    if (isFree()) {
        System.out.println("go to travel.");
        return;
    }
    System.out.println("stay at home to learn Java");
    return;
}

1.8 注释规约

第一条:【强制】 类、类属性、类方法的注释必须使用 Javadoc 规范,使用 /**内容*/ 格式,不得使用 //xxx 方式

image.png

第二条:【强制】 所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释,除了返回值、参数、异常说明外,还必须指出该方法做什么事情,实现什么功能。

第三条:【强制】 所有的类都必须添加创建者和创建日期。

image.png

1.9 其他

第三条:【强制】 后台输送给网页的变量必须加$!{var}——中间是感叹号

第四条:【强制】 注意 Math.random() 这个方法返回的是 double 类型,取值的范围 0≤x<1(能够取到零值,注意除零异常),如果向获取整数类型的随机数,不要将 x 放大 10 的若干倍然后取整,直接使用 Random 对象的 nextInt 或者 nextLong 方法。

第六条:【推荐】 不要在视图模板中加入任何复杂的逻辑。


第4章:安全规约

“安全生产,责任重于泰山。” 这句话同样适用于软件生产,本章主要说明编程中需要注意的比较基础的安全准则。

第一条:【强制】 隶属于用户个人的页面或者功能必须进行权限控制校验

第二条:【强制】 用户敏感数据禁止直接展示,必须对展示数据进行脱敏。

第三条:【强制】 用户输入的 SQL 参数严格使用参数绑定或者 METADATA 字段值限定,防止 SQL 注入,禁止字符串拼接 SQL 访问数据库。

image.png

第四条:【强制】 用户请求传入的任何参数必须做有效性验证

image.png

第五条:【强制】 禁止向 HTML 页面输出未经安全过滤或未正确转义的用户数据。

第六条:【强制】 表单、AJAX 提交必须执行 CSRF 安全过滤

[图片上传失败...(image-c3804-1533370102283)]

第七条:【强制】 在使用平台资源,譬如短信、邮件、电话、下单、支付,必须实现正确的防重放限制,如数量限制、疲劳度控制、验证码校验,避免被滥刷、资损。


第5章:MySQL 数据库

5.1 建表规约

第二条:【强制】 表名、字段名必须使用小写字母或数字 , 禁止出现数字开头,禁止两个下划线中间只出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。

正例: getter _ admin , task _ config , level 3_ name
反例: GetterAdmin , taskConfig , level 3 name

第四条:【强制】禁用保留字,如 desc 、 range 、 match 、 delayed 等,请参考 MySQL 官方保留字。

第五条: 【强制】主键索引名为 pk_ 字段名;唯一索引名为 uk _字段名 ; 普通索引名则为 idx _字段名。

第六条:【强制】小数类型为 decimal ,禁止使用 float 和 double 。

第八条:【强制】 varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长度大于此值,定义字段类型为 text ,独立出来一张表,用主键来对应,避免影响其它字段索引效率。

第九条:【强制】表必备三字段: id , gmt _ create , gmt _ modified

第十条: 【推荐】表的命名最好是加上“业务名称_表的作用”。

正例: tiger _ task / tiger _ reader / mpp _ config

第十五条:【参考】合适的字符存储长度,不但节约数据库表空间、节约索引存储,更重要的是提升检索速度。

正例:如下表,其中无符号值可以避免误存负数,且扩大了表示范围。

对象 年龄区间 类型 表示范围
150 岁之内 unsigned tinyint 无符号值:0 到 255
数百岁 unsigned smallint 无符号值:0 到 65535
恐龙化石 数千万年 unsigned int 无符号值:0 到约 42.9 亿
太阳 约 50 亿年 unsigned bigint 无符号值:0 到约 10 的 19 次方

5.2 索引规约

第五条: 【推荐】如果有 order by 的场景,请注意利用索引的有序性。 order by 最后的字段是组合索引的一部分,并且放在索引组合顺序的最后,避免出现 file _ sort 的情况,影响查询性能。

正例: where a =? and b =? order by c; 索引: a _ b _ c
反例:索引中有范围查找,那么索引有序性无法利用,如: WHERE a >10 ORDER BY b; 索引 a _ b 无法排序。

第九条: 【推荐】建组合索引的时候,区分度最高的在最左边。

正例:如果 where a =? and b =? , a 列的几乎接近于唯一值,那么只需要单建 idx _ a 索引即可。

5.3 SQL 语句

第一条:【强制】不要使用 count( 列名 ) 或 count( 常量 ) 来替代 count( * ) , count( * ) 是 SQL 92 定义的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。

第六条: 【强制】不得使用外键与级联,一切外键概念必须在应用层解决。

第八条: 【强制】数据订正时,删除和修改记录时,要先 select ,避免出现误删除,确认无误才能执行更新语句。

image.png

5.4 ORM 映射

整个规约对自己来说都挺有用的,因为正好涉及到这方面,幸好感觉脸不怎么疼。

第一条:【强制】在表查询中,一律不要使用 * 作为查询的字段列表,需要哪些字段必须明确写明。

第二条:【强制】 POJO 类的 布尔 属性不能加 is ,而数据库字段必须加 is _,要求在 resultMap 中进行字段与属性之间的映射。

第三条:【强制】不要用 resultClass 当返回参数,即使所有类属性名与数据库字段一一对应,也需要定义 ; 反过来,每一个表也必然有一个与之对应。

第七条:【强制】更新数据表记录时,必须同时更新记录对应的 gmt _ modified 字段值为当前时间。

第九条:【参考】@ Transactional 事务不要滥用。事务会影响数据库的 QPS ,另外使用事务的地方需要考虑各方面的回滚方案,包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等。


总结

浏览了一遍,还是学习到了很多东西吧,上面也仅仅只是总结了对我自己比较收益,现阶段我能吸收能实际感受得到的规约,如果想要 PDF 版的可以在这里下载:戳这里

上一篇下一篇

猜你喜欢

热点阅读