Java优雅编码
让代码性能更高
- 需要 Map 的主键和取值时,应该迭代 entrySet()
- 应该使用Collection.isEmpty()检测空
- 不要把集合对象传给自己
- 集合初始化尽量指定大小
- 字符串拼接使用 StringBuilder
- 判断链表还是数组
- 频繁调用 Collection.contains 方法请使用 Set
- 直接赋值常量值,禁止声明新对象
- 当成员变量值无需改变时,尽量定义为静态常量
- 尽量使用基本数据类型,避免自动装箱和拆箱
- 如果变量的初值会被覆盖,就没有必要给变量赋初值
- 尽量使用函数内的基本类型临时变量
- 尽量不要在循环体外定义变量
- 不可变的静态常量,尽量使用非线程安全类
- 尽量避免定义不必要的子类
- 尽量指定类的final修饰符
- 把跟类成员变量无关的方法声明成静态方法
- 尽量使用基本数据类型作为方法参数类型,避免不必要的装箱、拆箱和空指针判断
- 协议方法参数值非空,避免不必要的空指针判断
- 尽量避免不必要的函数封装
- 尽量减少方法的重复调用
- 尽量使用移位来代替正整数乘除
- 尽量不在条件表达式中使用!取反
- 对于多常量选择分支,尽量使用switch语句而不是if-else语句
- 尽量不要使用正则表达式匹配
- 不要使用循环拷贝数组,尽量使用System.arraycopy拷贝数组
- 集合转化为类型T数组时,尽量传入空数组T[0]
- 不要使用循环拷贝集合,尽量使用JDK提供的方法拷贝集合
- 尽量重复使用同一缓冲区
- 尽量使用缓冲流减少IO操作
- 在单线程中,尽量使用非线程安全类
- 在多线程中,尽量使用线程安全类
- 尽量减少同步代码块范围
- 尽量使用线程池减少线程开销
让代码更优雅 - 长整型常量后添加大写 L
- 不要使用魔法值
- 不要使用集合实现来赋值静态成员变量
- 建议使用 try-with-resources 语句
- 删除未使用的方法、参数、变量
- 公有静态常量应该通过类访问
- 使用String.valueOf(value)代替""+value
- 过时代码添加 @Deprecated 注解
- 尽量避免在循环中捕获异常
让代码远离 bug - 禁止使用构造方法 BigDecimal(double)
- 返回空数组和空集合而不是 null
- 优先使用常量或确定值来调用 equals 方法
- 枚举的属性字段必须是私有不可变
- 小心String.split(String regex)
让代码性能更高 - 需要 Map 的主键和取值时,应该迭代 entrySet()
反例:
Map<String, String> map = …;
for (String key : map.keySet()) {
String value = map.get(key);
…
}
1
2
3
4
5
正例
Map<String, String> map = …;
for (Map.Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
…
}
1
2
3
4
5
6
2. 应该使用Collection.isEmpty()检测空
反例:
if (collection.size() == 0) {
…
}
1
2
3
正例:
if (collection.isEmpty()) {
…
}
1
2
3
如果需要还需要检测 null ,可采用CollectionUtils.isEmpty(collection)和CollectionUtils.isNotEmpty(collection)。
- 不要把集合对象传给自己
反例:
List list = new ArrayList<>();
list.add(“Hello”);
list.add(“World”);
if (list.containsAll(list)) { // 无意义,总是返回true
…
}
list.removeAll(list); // 性能差, 直接使用clear()
1
2
3
4
5
6
7
4. 集合初始化尽量指定大小
Java集合初始化时都会指定一个默认大小,当默认大小不再满足数据需求时就会扩容,每次扩容的时间复杂度有可能是O(n)
反例:
List userDOList = …;
Set userSet = new HashSet<>();
Map<Long, UserDO> userMap = new HashMap<>();
List userList = new ArrayList<>();
for (UserDO userDO : userDOList) {
userSet.add(userDO.getId());
userMap.put(userDO.getId(), userDO);
userList.add(transUser(userDO));
}
1
2
3
4
5
6
7
8
9
正例:
List userDOList = …;
int userSize = userDOList.size();
Set userSet = new HashSet<>(userSize);
Map<Long, UserDO> userMap = new HashMap<>((int) Math.ceil(userSize * 4.0 / 3));
List userList = new ArrayList<>(userSize);
for (UserDO userDO : userDOList) {
userSet.add(userDO.getId());
userMap.put(userDO.getId(), userDO);
userList.add(transUser(userDO));
}
1
2
3
4
5
6
7
8
9
10
5. 字符串拼接使用 StringBuilder
反例:
String s = “”;
for (int i = 0; i < 10; i++) {
s += i;
}
1
2
3
4
正例:
String a = “a”;
String b = “b”;
String c = “c”;
String s = a + b + c; // 没问题,java编译器会进行优化
StringBuilder sb = new StringBuilder(50); //尽量设置初始化大小
for (int i = 0; i < 10; i++) {
sb.append(i);
}
1
2
3
4
5
6
7
8
6. 判断链表还是数组
正例:
// 调用别人的服务获取到list
List list = otherService.getList();
if (list instanceof RandomAccess) {
// 内部数组实现,可以随机访问
System.out.println(list.get(list.size() - 1));
} else {
// 内部可能是链表实现,随机访问效率低
}
1
2
3
4
5
6
7
8
7. 频繁调用 Collection.contains 方法请使用 Set
反例:
ArrayList list = otherService.getList();
for (int i = 0; i <= Integer.MAX_VALUE; i++) {
// 时间复杂度O(n)
list.contains(i);
}
1
2
3
4
5
正例:
ArrayList list = otherService.getList();
Set set = new HashSet(list);
for (int i = 0; i <= Integer.MAX_VALUE; i++) {
// 时间复杂度O(1)
set.contains(i);
}
1
2
3
4
5
6
8. 直接赋值常量值,禁止声明新对象
直接赋值常量值,只是创建了一个对象引用,而这个对象引用指向常量值。
反例:
Long i = new Long(1L);
String s = new String(“abc”);
1
2
正例:
Long i = 1L;
String s = “abc”;
1
2
9. 当成员变量值无需改变时,尽量定义为静态常量
在类的每个对象实例中,每个成员变量都有一份副本,而成员静态常量只有一份实例
反例:
public class HttpConnection {
private final long timeout = 5L;
…
}
1
2
3
4
正例:
public class HttpConnection {
private static final long TIMEOUT = 5L;
…
}
1
2
3
4
5
10. 尽量使用基本数据类型,避免自动装箱和拆箱
装箱和拆箱都是需要CPU和内存资源的,所以应尽量避免使用自动装箱和拆箱
反例:
Integer sum = 0;
int[] values = …;
for (int value : values) {
sum += value; // 相当于result = Integer.valueOf(result.intValue() + value);
}
1
2
3
4
5<