阿里巴巴Java开发手册阅读笔记--编程规约

本文是对《阿里巴巴Java开发手册》阅读过程的札记(记录我觉得重要的知识点以及我在实际开发过程中对这些问题的理解)

一、OOP规约篇

1、Object的equals方法容易抛空指针异常,应使用常量或确定有值的对象来调用equals。
正例:“test”.equals(object) 反例:object.equals(“test”)

2、所有的相同类型的包装类对象之间值的比较,全部使用 equals 方法比较。
说明:对于 Integer var =?在-128 至 127 之间的赋值, Integer 对象是在
IntegerCache . cache 产生,会复用已有对象,这个区间内的 Integer 值可以直接使用==进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用 equals 方法进行判断。
这里需要注意,Integer对象的值在-128 至 127范围内,会用常量区的值,否则就会生成新对象,很坑!

    Integer a = 120,b = 120,c = 150,d = 150;
    System.out.println(a == b);
    System.out.println(c == d);
    结果为:true    false

3、关于基本数据类型与包装数据类型的使用标准如下:
1 ) 所有的 POJO 类属性必须使用包装数据类型。
2 ) RPC 方法的返回值和参数必须使用包装数据类型。
3 ) 所有的局部变量【推荐】使用基本数据类型。
说明: POJO 类属性没有初值是提醒使用者在需要使用时,必须自己显式地进行赋值,任何NPE 问题,或者入库检查,都由使用者来保证。
正例:数据库的查询结果可能是 null ,因为自动拆箱,用基本数据类型接收有 NPE 风险。
反例:比如显示成交总额涨跌情况,即正负 x %, x 为基本数据类型,调用的 RPC 服务,调用不成功时,返回的是默认值,页面显示:0%,这是不合理的,应该显示成中划线-。所以包装数据类型的 null 值,能够表示额外的信息,如:远程调用失败,异常退出。
POJO类:(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBeans,是为了避免和EJB混淆所创造的简称。
NPE问题:java.lang.NullPointerException:NPE ,空值异常。

4、定义 DO / DTO / VO 等 POJO 类时,不要设定任何属性默认值。
反例: POJO 类的 gmtCreate 默认值为 new Date(); 但是这个属性在数据提取时并没有置入具体值,在更新其它字段时又附带更新了此字段,导致创建时间被修改成当前时间。

5、序列化类新增属性时,请不要修改 serialVersionUID 字段,避免反序列失败 ,实现Serializable接口时,会自动生成唯一的UID,不要更改; 如果完全不兼容升级,避免反序列化混乱,那么请修改 serialVersionUID 值。
说明:注意 serialVersionUID 不一致会抛出序列化运行时异常。

6、POJO 类必须写 toString 方法。使用 IDE 的中工具: source > generate toString时,如果继承了另一个 POJO 类,注意在前面加一下 super . toString 。说明:在方法执行抛出异常时,可以直接调用 POJO 的 toString() 方法打印其属性值,便于排查问题。其实,写POJO类时,一般默认都要写私有化字段,get/set方法,toString方法。无参构造器不写即默认省略

7、类内方法定义顺序依次是:公有方法或保护方法 > 私有方法 > getter / setter方法。
说明(一般编码规范):公有方法是类的调用者和维护者最关心的方法,首屏展示最好 ; 保护方法虽然只是子类关心,也可能是“模板设计模式”下的核心方法 ; 而私有方法外部一般不需要特别关心,是一个黑盒实现 ; 因为方法信息价值较低,所有 Service 和 DAO 的 getter / setter 方法放在类体最后。

8、循环体内,字符串的联接方式,使用 StringBuilder 的 append 方法进行扩展,切勿使用String对象直接用“+”拼接。
说明:反编译出的字节码文件显示每次循环都会 new 出一个 StringBuilder 对象,然后进行append 操作,最后通过 toString 方法返回 String 对象,造成内存资源浪费。

9、慎用 Object 的 clone 方法来拷贝对象。没用过这个方法,觉得很厉害的样子,Mark一下、
说明:对象的 clone 方法默认是浅拷贝,若想实现深拷贝需要重写 clone 方法实现属性对象的拷贝。

10、类成员与方法访问控制从严:比喻很到位
1 ) 如果不允许外部直接通过 new 来创建对象,那么构造方法必须是 private 。
2 ) 工具类不允许有 public 或 default 构造方法。
3 ) 类非 static 成员变量并且与子类共享,必须是 protected 。
4 ) 类非 static 成员变量并且仅在本类使用,必须是 private 。
5 ) 类 static 成员变量如果仅在本类使用,必须是 private 。
6 ) 若是 static 成员变量,必须考虑是否为 final 。
7 ) 类成员方法只供类内部调用,必须是 private 。
8 ) 类成员方法只对继承类公开,那么限制为 protected 。
说明:任何类、方法、参数、变量,严控访问范围。过宽泛的访问范围,不利于模块解耦。思
考:如果是一个 private 的方法,想删除就删除,可是一个 public 的 Service 方法,或者一
个 public 的成员变量,删除一下,不得手心冒点汗吗?变量像自己的小孩,尽量在自己的视
线内,变量作用域太大,如果无限制的到处跑,那么你会担心你的孩子。

二、集合问题处理篇

1、关于 hashCode 和 equals 的处理,遵循如下规则:

1) 只要重写 equals ,就必须重写 hashCode 。
2) 因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的
对象必须重写这两个方法。
3) 如果自定义对象做为 Map 的键,那么必须重写 hashCode 和 equals 。
正例: String 重写了 hashCode 和 equals 方法,所以我们可以非常愉快地使用 String 对象作为 key 来使用。

2、ArrayList 的 subList 结果不可强转成 ArrayList ,否则会抛出 ClassCastException
异常: java . util . RandomAccessSubList cannot be cast to java . util . ArrayList ;
说明: subList 返回的是 ArrayList 的内部类 SubList ,并不是 ArrayList ,而是ArrayList 的一个视图,对于 SubList 子列表的所有操作最终会反映到原列表上。

3、在 subList 场景中,高度注意对原集合元素个数的修改,会导致子列表的遍历、增加、删除均产生 ConcurrentModificationException 异常。

4、使用集合转数组的方法,必须使用集合的 toArray(T[] array) ,传入的是类型完全一样的数组,大小就是 list . size() 。
反例:直接使用 toArray 无参方法存在问题,此方法返回值只能是 Object[] 类,若强转其它类型数组将出现 ClassCastException 错误。
正例:

List list = new ArrayList(2);
list.add("FLY");
list.add("fly");
String[] array = new String[list.size()];
array = list.toArray(array);

说明:使用 toArray 带参方法,入参分配的数组空间不够大时, toArray 方法内部将重新分配内存空间,并返回新数组地址 ; 如果数组元素大于实际所需,下标为 [ list . size() ] 的数组元素将被置为 null ,其它数组元素保持原值,因此最好将方法入参数组大小定义与集合元素个数一致。

5、使用工具类 Arrays . asList() 把数组转换成集合时,不能使用其修改集合相关的方法,它的 add / remove / clear 方法会抛出 UnsupportedOperationException 异常。
说明: asList 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。 Arrays . asList
体现的是适配器模式,只是转换接口,后台的数据仍是数组。

String[] str = new String[] { "a", "b" };
List list = Arrays.asList(str);

第一种情况: list.add(“c”); 运行时异常。
第二种情况: str[0]= “Timor”; 那么 list.get(0) 也会随之修改。

6、泛型通配符来接收返回的数据,此写法的泛型集合不能使用 add 方法。
说明:苹果装箱后返回一个对象,此对象就不能往里加任何水果,包括苹果。

7、不要在 foreach 循环里进行元素的 remove / add 操作。 remove 元素请使用 Iterator方式,如果并发操作,需要对 Iterator 对象加锁。
反例:

List<String> a = new ArrayList<String>();
a.add("1");
a.add("2");
for (String temp : a) {
    if("1".equals(temp)){
    a.remove(temp);
    }
}
可以删除1,输出[2],把判断条件的1换成2之后就会报异常。 异常信息:java.util.ConcurrentModificationException

正例:

Iterator it = a.iterator();
while(it.hasNext()){
String temp = it.next();
if(删除元素的条件){
    it.remove();
    }
}

8、遍历Map集合时,尽量使用entrySet得到所有KV的集合,再进行遍历,对大数据量的集合遍历有速度优势。

9、高度注意 Map 类集合 K / V 能不能存储 null 值的情况,如下:
阿里巴巴Java开发手册阅读笔记--编程规约_第1张图片

10、利用 Set 元素唯一的特性,可以快速对一个集合进行去重操作,避免使用 List 的contains 方法进行遍历、对比、去重操作。
关于集合的遍历及其他问题请参照我的其他博文

你可能感兴趣的:(Java)