美文网首页
阿里巴巴Java开发手册关键点解析

阿里巴巴Java开发手册关键点解析

作者: 今有所思 | 来源:发表于2019-05-04 22:22 被阅读0次

    1 编程规约

    1.1 命名风格

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

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

    解析:Boolean和boolean的getter/setter方法名字不一样,不同的序列化框架解释规则也不一致,导致解析时,变量名会不一致。为什么阿里巴巴禁止开发人员使用isSuccess作为变量名

    public class App {
        Boolean isSuccess;
        boolean isHappy;
    
        public Boolean getSuccess() {
            return isSuccess;
        }
    
        public void setSuccess(Boolean success) {
            isSuccess = success;
        }
    
        public boolean isHappy() {
            return isHappy;
        }
    
        public void setHappy(boolean happy) {
            isHappy = happy;
        }
    }
    

    16.【参考】各层命名规约:

    • Service/DAO层方法命名规约

      1. 获取单个对象的方法用get做前缀。
      2. 获取多个对象的方法用list做前缀,复数形式结尾如:listObjects。
      3. 获取统计值的方法用count做前缀。
      4. 插入的方法用save/insert做前缀。
      5. 删除的方法用remove/delete做前缀。
      6. 修改的方法用update做前缀。
    • 领域模型命名规约:

      1. 数据对象:xxxDO,xxx即为数据表名。
      2. 数据传输对象:xxxDTO,xxx为业务领域相关的名称。
      3. 展示对象:xxxVO,xxx一般为网页名称。
      4. POJO是DO/DTO/BO/VO的统称,禁止命名成xxxPOJO。

    解析: DO一般就是DAO层数据,一般就是数据库表名,
    VO是用来与前端交互的参数;而
    DTO一般是用来传输的,即DO与DTO,VO与DTO之间的相互转化。

    1.2 常量定义

    1.【强制】不允许任何魔法值(即未经预先定义的常量)直接出现在代码中。
    反例:

    String key = "Id#taobao_" + tradeId;
    cache.put(key, value);  
    

    解析:魔法值不易于维护,而且用起来也不直观。比如说,这个key在该类中其他很多地方都使用了,如果要修改,就要改很多地方,容易出错,也麻烦。

    1.3 代码格式

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

    1.4 OOP规约

    1. 【强制】相同参数类型,相同业务含义,才可以使用 Java 的可变参数,避免使用 Object。

    说明:可变参数必须放置在参数列表的最后。(提倡同学们尽量不用可变参数编程)
    正例:

    public List<User> listUsers(String type, Long... ids) {...}
    
    1. 【强制】所有的相同类型的包装类对象之间值的比较,全部使用 equals 方法比较。

    说明:对于 Integer var = ? 在-128 至 127 范围内的赋值,Integer 对象是在 IntegerCache.cache 产生,会复用已有对象,这个区间内的 Integer 值可以直接使用==进行 判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑, 推荐使用 equals 方法进行判断。

    1. 关于基本数据类型与包装数据类型的使用标准如下:
      1. 【强制】所有的POJO类属性必须使用包装数据类型。
      2. 【强制】RPC方法的返回值和参数必须使用包装数据类型。
      3. 【推荐】所有的局部变量使用基本数据类型。

    说明:POJO 类属性没有初值是提醒使用者在需要使用时,必须自己显式地进行赋值,任何NPE问题,或者入库检查,都由使用者来保证。

    正例:数据库的查询结果可能是 null,因为自动拆箱,用基本数据类型接收有 NPE 风险。

    反例:比如显示成交总额涨跌情况,即正负 x%,x 为基本数据类型,调用的 RPC 服务,调用
    不成功时,返回的是默认值,页面显示为 0%,这是不合理的,应该显示成中划线。所以包装 数据类型的 null 值,能够表示额外的信息,如:远程调用失败,异常退出。

    1.5 集合处理

    1. 【强制】ArrayList的subList结果不可强转成ArrayList,否则会抛出ClassCastException 异常,即java.util.RandomAccessSubList cannot be cast to java.util.ArrayList。

    说明:subList 返回的是 ArrayList 的内部类 SubList,并不是 ArrayList 而是 ArrayList 的一个视图,对于 SubList 子列表的所有操作最终会反映到原列表上。

    1. 【强制】在 subList 场景中,高度注意对原集合元素的增加或删除,均会导致子列表的遍历、 增加、删除产生ConcurrentModificationException 异常。
    1. 【强制】不要在 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 (删除元素的条件) {
                   iterator.remove();
               }
     }
    

    反例:

    for (String item : list) { 
        if ("1".equals(item)) {
            list.remove(item);
        }
    } 
    

    说明:以上代码的执行结果肯定会出乎大家的意料,那么试一下把“1”换成“2”,会是同样的
    结果吗?

    1. 【强制】 在 JDK7 版本及以上,Comparator 实现类要满足如下三个条件,不然 Arrays.sort, Collections.sort 会报 IllegalArgumentException 异常。

    说明:三个条件如下
    1) x,y的比较结果和y,x的比较结果相反。
    2) x>y,y>z,则x>z。
    3) x=y,则x,z比较结果和y,z比较结果相同。

    反例:下例中没有处理相等的情况,实际使用中可能会出现异常:

    new Comparator<Student>() {
        @Override
        public int compare(Student o1, Student o2) {
            return o1.getId() > o2.getId() ? 1 : -1; 
        }
    };
    
    1. 【推荐】集合初始化时,指定集合初始值大小。

      说明:HashMap使用HashMap(int initialCapacity) 初始化。

    正例:initialCapacity = (需要存储的元素个数 / 负载因子) + 1。注意负载因子(即 loader factor)默认为 0.75,如果暂时无法确定初始值大小,请设置为 16(即默认值)。 反例:HashMap 需要放置 1024 个元素,由于没有设置容量初始大小,随着元素不断增加,容 量 7 次被迫扩大,resize 需要重建 hash 表,严重影响性能。

    1. 【推荐】使用 entrySet 遍历 Map 类集合 KV,而不是 keySet 方式进行遍历。

    说明:keySet 其实是遍历了 2 次,一次是转为 Iterator 对象,另一次是从 hashMap 中取出 key 所对应的 value。而 entrySet 只是遍历了一次就把 key 和 value 都放到了 entry 中,效 率更高。如果是 JDK8,使用 Map.foreach 方法。

    正例:values()返回的是 V 值集合,是一个 list 集合对象;keySet()返回的是 K 值集合,是 一个 Set 集合对象;entrySet()返回的是 K-V 值组合集合。

    1. 【推荐】高度注意 Map 类集合 K/V 能不能存储 null 值的情况,如下表格:
    集合类 Key Value
    Hashtable 不允许为 null 不允许为 null
    ConcurrentHashMap 不允许为 null 不允许为 null
    TreeMap 不允许为 null 允许为 null
    HashMap 允许为 null 允许为 null

    反例: 由于 HashMap 的干扰,很多人认为 ConcurrentHashMap 是可以置入 null 值,而事实上, 存储 null 值时会抛出 NPE 异常。

    1. 【强制】获取当前毫秒数 System.currentTimeMillis(); 而不是 new Date().getTime();

    说明:如果想获取更加精确的纳秒级时间值,使用 System.nanoTime()的方式。在 JDK8 中, 针对统计时间等场景,推荐使用 Instant 类。

    2 异常日志

    1. 【强制】catch 时请分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码。 对于非稳定代码的 catch 尽可能进行区分异常类型,再做对应的异常处理。

    说明:对大段代码进行 try-catch,使程序无法根据不同的异常做出正确的应激反应,也不利 于定位问题,这是一种不负责任的表现。 正例:用户注册的场景中,如果用户输入非法字符,或用户名称已存在,或用户输入密码过于 简单,在程序上作出分门别类的判断,并提示给用户。

    1. 【强制】捕获异常与抛异常,必须是完全匹配,或者捕获异常是抛异常的父类。

    说明:如果预期对方抛的是绣球,实际接到的是铅球,就会产生意外情况。

    1. 【推荐】防止 NPE,是程序员的基本修养,注意 NPE 产生的场景:
      1)返回类型为基本数据类型,return 包装数据类型的对象时,自动拆箱有可能产生 NPE。
      - 反例:public int f() { return Integer 对象}, 如果为 null,自动解箱抛 NPE。
      1. 数据库的查询结果可能为null。
      2. 集合里的元素即使isNotEmpty,取出的数据元素也可能为null。
      3. 远程调用返回对象时,一律要求进行空指针判断,防止NPE。
      4. 对于Session中获取的数据,建议NPE检查,避免空指针。
      5. 级联调用obj.getA().getB().getC();一连串调用,易产生NPE。
        正例:使用 JDK8 的 Optional 类来防止 NPE 问题。

    3 单元测试

    1. 【强制】好的单元测试必须遵守 AIR 原则。

    说明:单元测试在线上运行时,感觉像空气(AIR)一样并不存在,但在测试质量的保障上, 却是非常关键的。好的单元测试宏观上来说,具有自动化、独立性、可重复执行的特点。

    • A:Automatic(自动化)
    • I:Independent(独立性)
    • R:Repeatable(可重复)
    1. 【推荐】编写单元测试代码遵守 BCDE 原则,以保证被测试模块的交付质量。
    • B:Border,边界值测试,包括循环边界、特殊取值、特殊时间点、数据顺序等。
    • C:Correct,正确的输入,并得到预期的结果。
    • D:Design,与设计文档相结合,来编写单元测试。
    • E:Error,强制错误信息输入(如:非法数据、异常流程、非业务允许输入等),并得 到预期的结果。

    4 安全规约

    5 MySQL数据库

    5.1 建表约束

    1. 【强制】主键索引名为 pk_字段名;唯一索引名为 uk_字段名;普通索引名则为 idx_字段名。

    说明:pk_ 即 primary key;uk_ 即 unique key;idx_ 即 index 的简称。

    5.2 索引规约

    8.【推荐】SQL 性能优化的目标:至少要达到 range 级别,要求是 ref 级别,如果可以是 consts 最好。
    说明:

    1. consts 单表中最多只有一个匹配行(主键或者唯一索引),在优化阶段即可读取到数据。
    2. ref 指的是使用普通的索引(normal index)。
    3. range 对索引进行范围检索。

    反例:explain 表的结果,type=index,索引物理文件全扫描,速度非常慢,这个 index 级 别比较 range 还低,与全表扫描是小巫见大巫。

    6 工程结构

    7 设计规约

    相关文章

      网友评论

          本文标题:阿里巴巴Java开发手册关键点解析

          本文链接:https://www.haomeiwen.com/subject/yffqoqtx.html