日常编码Java规范<二>

48 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第3天,点击查看活动详情

1、new BigDecimal(double) 存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常。请使用 BigDecimal.valueOf(double) 。

2、SimpleDateFormat 是线程不安全的类,一般不要定义为 static 变量,如果定义为static,必须加锁。如果是 JDK8 的应用,可以使用 Instant 代替 Date, LocalDateTime 代替 Calendar,DateTimeFormatter代替Simpledateformatter。

LocalDateTime localDateTime = LocalDateTime.now();
LocalDateTime atTime = localDate.atTime(LocalTime.MAX);
LocalDateTime atDate = localTime.atDate(LocalDate.of(2019, 10, 14));

// 时间日期格式化,相当于 java.text.SimpleDateFormat
LocalDateTime dateTime = LocalDateTime.now();
dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));

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

4、Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用equals,如"test".equals(object);或者使用StringUtils.equals("1","2")

5、序列化类新增属性时,请不要修改 serialVersionUID 字段,避免反序列失败; 如果完全不兼容升级,避免反序列化混乱,那么请修改 serialVersionUID 值。注意 serialVersionUID 不一致会抛出序列化运行时异常。

6、关于基本数据类型与包装数据类型的使用标准如下:

1) 【强制】 所有的 POJO 类属性必须使用包装数据类

2) 【强制】 RPC 方法的返回值和参数必须使用包装类

3) 【推荐】 所有的局部变量使用基本数据类型。

用基本数据类型数据默认值是0,而包装数据类型默认值是null,数据库的查询结果可能为null,因为自动拆箱,用基本数据类型接收有NPE风险。

7、慎用 Object 的 clone 方法来拷贝对象。对象的 clone 方法默认是浅拷贝,若想实现深拷贝需要重写 clone 方法实现属性对象的拷贝。

8、ArrayList的subList结果不可强转成ArrayList,否则会抛出 ClassCastException异常: java.util.RandomAccessSubList cannot be cast to java.util.ArrayList。subList 返回的是 ArrayList 的内部类 SubList,并不是 ArrayList ,而是ArrayList 的一个视图,对于 SubList 子列表的所有操作最终会反映到原列表上。在 subList 场景中, 高度注意对原集合元素个数的修改,会导致子列表的遍历、增加、删除均产生 ConcurrentModificationException 异常。

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

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

11、集合初始化时, 指定集合初始值大小。

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

正例:initialCapacity = (需要存储的元素个数 / 负载因子) + 1。注意负载因子(即 loaderfactor) 默认为 0.75, 如果暂时无法确定初始值大小, 请设置为 16。

12、使用 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 值组合集合。

13、在 java 集合类库中,List 的 contains 方法普遍时间复杂度是 O(n) ,如果在代码中需要频繁调用 contains 方法查找数据,可以先将 list 转换成 HashSet 实现,将 O(n) 的时间复杂度降为 O(1) 。