一、避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可
- 通过一个引用去调用一个静态方法或静态变量的时候会加大JVM的性能消耗,对JVM来说,会先去检索一下堆中的信息,而且在解释过程中需要额外消耗一些资源的,如果用类名去调用会直接导入到方法区,可以避免堆空间的检索
二、所有的复写方法必须加@Override注解
三、相同参数类型,相同业务含义,才可以使用Java的可变参数,避免使用Object类型
- 可变参数对JDK的编译JVM的解释都是一个比较大的消耗,它会在JVM下进行一个类似于数组的转换,在解释编译的时候要预留各种本地信息,如果一定要用可变参数 绝对不要写Object类型
四、外部正在调用或者二方库依赖的接口,不允许修改方法签名,避免对接口调用方产生影响。接口过时必须加@Deprecated 注解,并清晰地说明采用的新接口或者新服务是什么
- 对于RPC开发和SOA架构模式来说,开发的每一块代码都可能是一个服务,服务需要对外暴露,服务发布之后,只要这个服务被使用了或者是被使用了,方法绝对不要改,可以重写或者重载来扩展服务能力
五、不能使用过时的类或方法
六、Object的equals方法容易抛空指针异常,应使用常量或确定有值的对象来调用equals
- 推荐使用java.util.Objects包中的equals()方法进行比较
七、所有的相同类型的包装类对象之间值的比较,全部使用equals方法比较
- 只要不是8种基本数据类型进行比较的时候都使用equals方法
- 只有在判断两个引用指向的是不是同一个 的时候 才能使用==
- equals比较的是内容数据 ==比较的是内存地址
八、关于基本数据类型与包装数据类型的使用标准
- 所有的pojo类属性必须使用包装数据类型
- 数据库的查询接口可能是null,因为自动拆箱,用基本数据类型接受有风险,
- PRC方法的返回值和参数必须使用包装数据类型
- 所有的局部变量使用基本数据类型
- 局部变量绝大多数情况下用作计算的,基本类型做计算是有天然优势的,不需要自动拆箱,自动封箱,避免出现空指针异常
九、定义DO/DTO/VO 等 POJO 类时,不要设定任何属性默认值
- VO(View Object):视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来。
- DTO(Data Transfer Object):数据传输对象,这个概念来源于J2EE的设计模式,原来的目的是为了EJB的分布式应用提供粗粒度的数据实体,以减少分布式调用的次数,从而提高分布式调用的性能和降低网络负载,但在这里,我泛指用于展示层与服务层之间的数据传输对象。
- DO(Domain Object):领域对象,就是从现实世界中抽象出来的有形或无形的业务实体。数据对象,和数据库中的表一一对应
十、序列化类新增属性时,请不要修改serialVersionUID字段,避免反序列化失败,如果完全不兼容升级,避免反序列化混乱,那么请修改serialVersionUID值
十一、构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在init方法中
十二、pojo类必须写toString方法,如果继承了另一个pojo类,注意在前面加一个super.toString
- 在方法执行抛出异常时,可以直接调用pojo的toString方法打印其属性值,便于排查问题
十三、使用索引访问用 String 的 split 方法得到的数组时,需做最后一个分隔符后有无内容的检查,否则会有抛 IndexOutOfBoundsException 的风险
String str = "a,b,c,,";
String[] ary = str.split(",");
// 预期大于 3,结果是 3
System.out.println(ary.length);
十四、循环体内,字符串的连接方式,使用StringBuilder的append方法进行扩展
- 反编译出的字节码文件显示每次循环都会new出一个StringBuilder对象,然后进行append操作,最后通过toString方法返回String对象,造成内存资源浪费
- StringBuilder不会造成常量池空间中不必要数据的堆积,减少不必要对象的创建
- 单线程时候使用:StringBuilder 多线程环境使用StringBuffer
十五、尽量不要用Object的clone方法来拷贝对象
- 对象的clone方法默认是浅拷贝,若想实现深拷贝需要重写clone方法实现属性对象的拷贝
十六、类成员与方法访问控制从严:
- 如果不允许外部直接通过 new 来创建对象,那么构造方法必须是 private。
- 工具类不允许有 public 或 default 构造方法。
- 类非 static 成员变量并且与子类共享,必须是 protected。
- 类非 static 成员变量并且仅在本类使用,必须是 private。
- 类 static 成员变量如果仅在本类使用,必须是 private。
- 若是 static 成员变量,必须考虑是否为 final。
- 类成员方法只供类内部调用,必须是 private。
- 类成员方法只对继承类公开,那么限制为 protected。
集合处理
一、关于hashCode和equals的处理
- 只要重写 equals,就必须重写 hashCode。
- 因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的对象必须重写这两个方法。
- 如果自定义对象作为 Map 的键,那么必须重写 hashCode 和 equals
说明:String 重写了 hashCode 和 equals 方法,所以我们可以非常愉快地使用 String 对象作为 key 来使用
二、在 subList 场景中,高度注意对原集合元素个数的修改,会导致子列表的遍历、增加、删除均会产生 ConcurrentModificationException 异常。
标题三、使用集合转数组的方法,必须使用集合的 toArray(T[] array),传入的是类型完全一样的数组,大小就是 list.size()。
List<String> list = new ArrayList<String>(2);
list.add("guan");
list.add("bao");
String[] array = new String[list.size()];
array = list.toArray(array);
- 直接使用 toArray 无参方法存在问题,此方法返回值只能是 Object[]类,若强转其它类型数组将出现 ClassCastException 错误
四、使用工具类 Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的 add/remove/clear 方法会抛出 UnsupportedOperationException 异常。
-
说明:asList 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。Arrays.asList体现的是适配器模式,只是转换接口,后台的数据仍是数组。
String[] str = new String[] { “you”, “wu” };
List list = Arrays.asList(str); -
第一种情况:list.add(“yangguanbao”); 运行时异常。
-
第二种情况:str[0] = “gujin”; 那么 list.get(0)也会随之修改
五、泛型通配符<? extends T>来接收返回的数据,此写法的泛型集合不能使用 add 方法,而<? super T>不能使用 get 方法,作为接口调用赋值时易出错。
- 说明:扩展说一下 PECS(Producer Extends Consumer Super)原则:第一、频繁往外读取内容的,适合用<? extends T>。第二、经常往里插入的,适合用<? super T>。
六、不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator方式,如果并发操作,需要对 Iterator 对象加锁。
七、在 JDK7 版本及以上,Comparator 要满足如下三个条件,不然 Arrays.sort,Collections.sort 会报 IllegalArgumentException 异常
- 一定要计算好排序规则
八、集合初始化时,指定集合初始值大小。
- 说明:HashMap 使用 HashMap(int initialCapacity) 初始化,
- 正例:initialCapacity = (需要存储的元素个数 / 负载因子) + 1。注意负载因子(即 loader factor)默认为 0.75,如果暂时无法确定初始值大小,请设置为 16(即默认值)。
- 反例:HashMap 需要放置 1024 个元素,由于没有设置容量初始大小,随着元素不断增加,容量 7 次被迫扩大,resize 需要重建 hash 表,严重影响性能
九、使用 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 值组合集合。
十.高度注意 Map 类集合 K/V 能不能存储 null 值的情况,如下表格:
集合类 | Key | Value | Super | 说明 |
---|---|---|---|---|
Hashtable | 不允许为 null | 不允许为 null | Dictionary | 线程安全 |
ConcurrentHashMap | 不允许为 null | 不允许为 null | AbstractMap | 锁分段技术(JDK8:CAS) |
TreeMap | 不允许为 null | 允许为 null | AbstractMap | AbstractMap 线程不安全 |
HashMap | 允许为 null | 允许为 null | AbstractMap | AbstractMap 线程不安全 |
十一.合理利用好集合的有序性(sort)和稳定性(order),避免集合的无序性(unsort)和不稳定性(unorder)带来的负面影响。
- 说明:有序性是指遍历的结果是按某种比较规则依次排列的。稳定性指集合每次遍历的元素次序是一定的。如:ArrayList 是 order/unsort;HashMap 是 unorder/unsort;TreeSet 是order/sort。