- 直接赋值常量值,禁止声明新对象(直接赋值常量值,只是创建了一个对象引用,而这个对象引用指向常量值。)
反例:String s = new String("abc");
正例:String s = "abc";
- 当成员变量值无需改变时,尽量定义为静态常量(在类的每个对象实例中,每个成员变量都有一份副本,而成员静态常量只有一份实例。)
反例: private final long timeout = 5L;
正例:private static final long TIMEOUT = 5L;
- 尽量使用基本数据类型,避免自动装箱和拆箱(Java 中的基本数据类型double、float、long、int、short、char、boolean,分别对应包装类Double、Float、Long、Integer、Short、Character、Boolean。JVM支持基本类型与对应包装类的自动转换,被称为自动装箱和拆箱。装箱和拆箱都是需要CPU和内存资源的,所以应尽量避免使用自动装箱和拆箱。)
- 如果变量的初值会被覆盖,就没有必要给变量赋初值
反例:List<UserDO> userList = new ArrayList<>();
正例:List<UserDO> userList;
- 禁止使用JSON转化对象(JSON提供把对象转化为JSON字符串、把JSON字符串转为对象的功能,于是被某些人用来转化对象。这种对象转化方式,虽然在功能上没有问题,但是在性能上却存在问题。)
反例:List<UserDO> userDOList = ...;
List<UserVO>
userVOList = JSON.parseArray(JSON.toJSONString(userDOList), UserVO.class);
正例:List<UserDO> userDOList = ...;
List<UserVO> userVOList = new ArrayList<>(userDOList.size());
for (UserDO userDO : userDOList) {
UserVO userVO = new UserVO();
userVO.setId(userDO.getId());
...
userVOList.add(userVO);
}
- 采用Lambda表达式替换内部匿名类(对于大多数刚接触JDK8的同学来说,都会认为Lambda表达式就是匿名内部类的语法糖。实际上, Lambda表达式在大多数虚拟机中采用invokeDynamic指令实现,相对于匿名内部类在效率上会更高一些。)
反例:
List<User> userList = ...;
Collections.sort(userList, new Comparator<User>() {
@Override
public int compare(User user1, User user2) {
Long userId1 = user1.getId();
Long userId2 = user2.getId();
...
return userId1.compareTo(userId2);
}
});
正例:List<User> userList = ...;
Collections.sort(userList, (user1, user2) -> {
Long userId1 = user1.getId();
Long userId2 = user2.getId();
...
return userId1.compareTo(userId2);
});
- .尽量指定类的final修饰符(为类指定final修饰符,可以让该类不可以被继承。如果指定了一个类为final,则该类所有的方法都是final的,Java编译器会寻找机会内联所有的final方法。内联对于提升Java运行效率作用重大,具体可参见Java运行期优化,能够使性能平均提高50%。)
- 把跟类成员变量无关的方法声明成静态方法(静态方法的好处就是不用生成类的实例就可以直接调用。静态方法不再属于某个对象,而是属于它所在的类。只需要通过其类名就可以访问,不需要再消耗资源去反复创建对象。即便在类内部的私有方法,如果没有使用到类成员变量,也应该声明为静态方法。)
- 协议方法参数值非空,避免不必要的空指针判断(协议编程,可以@NonNull和@Nullable标注参数,是否遵循全凭调用者自觉。)
- 尽量减少方法的重复调用
- 对于多常量选择分支,尽量使用switch语句而不是if-else语句(if-else语句,每个if条件语句都要加装计算,直到if条件语句为true为止。switch语句进行了跳转优化,Java中采用tableswitch或lookupswitch指令实现,对于多常量选择分支处理效率更高。经过试验证明:在每个分支出现概率相同的情况下,低于5个分支时if-else语句效率更高,高于5个分支时switch语句效率更高。)
- 尽量使用StringBuilder进行字符串拼接(String是final类,内容不可修改,所以每次字符串拼接都会生成一个新对象。StringBuilder在初始化时申请了一块内存,以后的字符串拼接都在这块内存中执行,不会申请新内存和生成新对象。)
- 初始化集合时,尽量指定集合大小
- 不要使用size方法检测空,必须使用isEmpty方法检测空
- 尽量重复使用同一缓冲区(针对缓冲区,Java虚拟机需要花时间生成对象,还要花时间进行垃圾回收处理。所以,尽量重复利用缓冲区。)
- 利用 Set 的 add 方法的返回值,可以直接知道该值是否已经存在,可以避免调用 contains 方法判断存在。
SSet<Long> userIdSet = new HashSet<>();
List<UserVO> userVOList = new ArrayList<>();
for (UserDO userDO : userDOList) {
if (userIdSet.add(userDO.getId())) {
userVOList.add(transUser(userDO));
}
}
- 利用 Map 的 computeIfAbsent 方法,可以保证获取到的对象非空,从而避免了不必要的空判断和重新设置值。
普通做法: Map<Long, List<UserDO>> roleUserMap = new HashMap<>();
for (UserDO userDO : userDOList) {
Long roleId = userDO.getRoleId();
List<UserDO> userList = roleUserMap.get(roleId);
if (Objects.isNull(userList)) {
userList = new ArrayList<>();
roleUserMap.put(roleId, userList);
}
userList.add(userDO);}
优化做法: Map<Long, List<UserDO>> roleUserMap = new HashMap<>();
for (UserDO userDO : userDOList) {
roleUserMap.computeIfAbsent(userDO.getRoleId(), key -> new ArrayList<>())
.add(userDO);
}
- 空值判断
if (CollectionUtils.isNotEmpty(userList)) {
// TODO: 处理代码
}