1. 当成员变量值无需改变时,尽量定义为静态常量
- 在类的每个对象实例中,每个成员变量都有一份副本,而成员静态变量只有一份实例
- 反例
public class HttpConnection{
private final long timeout = 5L;
}
public class HttpConnection{
private static final long TIMEOUT = 5L;
}
2. 尽量使用基本数据类型,避免自动装箱拆箱和空指针的判断
- JVM支持基本数据类型和包装类的自动转换称为自动装箱和拆箱。装箱和拆箱都是需要消耗CPU和内存资源,所以应尽量避免使用自动拆装箱,其次还可以避免方法返回值的空指针判断。
- 反例
public static Integer isValid() {
Integer sum = 0;
int[] values = ...;
for (int value : values) {
sum += value;
}
return sum;
}
public static int isValid() {
int sum = 0;
int[] values = ...;
for (int value : values) {
sum += value;
}
return sum;
}
3. 尽量使用函数内的基本类型临时变量
- 在函数内,基本类型的参数和临时变量都保存在栈中,访问速度较快;对象类型的参数和临时变量的引用都保存在栈中,内容都保存在堆中,访问速度较慢。在类中,任何类型的成员变量都保存在堆中,访问速度较慢。
- 反例
public final class Accumulator {
private double result = 0.0D;
public void addAll(@NotNull double[] values) {
for (double value : values) {
result += value;
}
}
}
public final class Accumulator {
private double result = 0.0D;
public void addAll(@NotNull double[] values) {
double sum = 0.0D;
for (double value : values) {
sum += value;
}
result += sum;
}
}
4. 尽量不使用反射赋值对象
- 用反射赋值对象,主要优点是节省了代码量,主要缺点却是性能的下降。
- 反例
List<UserDO> userDOList = ...;
List<UserVO> userVOList = new ArrayList<>(userDOList.size());
for (UserDO userDO : userDOList) {
UserVO userVO = new UserVO();
BeanUtils.copyProperties(userDO, userVO);
userVOList.add(userVO);
}
List<UserDO> userDOList = ...;
List<UserVO> userVOList = new ArrayList<>(userDOList.size());
for (UserDO userDO : userDOList) {
userVOList.add(new UserVO(
userDO.getName(),
userDO.getPhone(),
userDO.getIcon()
));
}
5. 把跟类成员变量无关的方法声明成静态方法
- 静态方法的好处是不用生成类的实例就可以直接调用。静态方法不属于对象,而是属于类。只需要通过其类名就可以访问,不需要再消耗资源去反复创建对象。即便在类内部的私有方法,如果没有使用到类成员变量,也应该声明为静态方法。
- 反例
public int getMonth(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
return calendar.get(Calendar.MONTH) + 1;
}
public static int getMonth(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
return calendar.get(Calendar.MONTH) + 1;
}
6. 协议方法参数值非空,避免不必要的空指针判断
- 协议编程,可以用@NonNull和@Nullable标注参数,是否遵循全凭调用者自觉
- 反例
public static boolean isValid(UserDO user) {
if (Objects.isNull(user)) {
return false;
}
return Boolean.TRUE.equals(user.getIsValid());
}
public static boolean isValid(@NotNull UserDO user) {
return Boolean.TRUE.equals(user.getIsValid());
}
7. 尽量减少方法的重复调用
List<UserDO> userList = ...;
for (int i = 0; i < userList.size(); i++) {
...
}
List<UserDO> userList = ...;
int userLength = userList.size();
for (int i = 0; i < userLength; i++) {
...
}
8. 尽量避免不必要的方法调用
List<UserDO> userList = userDAO.queryActive();
if (isAll) {
userList = userDAO.queryAll();
}
List<UserDO> userList;
if (isAll) {
userList = userDAO.queryAll();
}else{
userList = userDAO.queryActive();
}
9. 尽量使用位移来代替正整数乘除
- 用位移操作可以极大地提高性能。对于乘除2^n(n为正整数)的计算,可以用位移操作来代替。
- 反例
int num1 = a * 4;
int num2 = a / 4;
int num1 = a << 2;
int num2 = a >> 2;
10. 对于多常量选择分支,尽量使用switch语句而不是if-else语句
- if-else语句,每个if条件都要加装计算,直到if条件语句为true为止。switch语句进行了跳转优化,对于多常量选择分支处理效率更高。经过经验证明:在每个分支出现概率相同的情况下,低于5个分支时if-else语句效率更高,高于5个时switch语句效率更高。
- 注意如果业务复杂,可以采用Map实现策略模式。
11. 不要使用" "+转化字符串
- 使用" "+进行字符串转化,使用方便但是效率低,建议使用String.valueOf,效率高还优雅。
- 反例
int i = 12345;
String s = " " + i;
int i = 12345;
String s = String.valueOf(i);
12. 尽量使用Arrays.asList转化数组为列表
public void demo() {
List<String> typeList = new ArrayList<>(8);
typeList.add("Short ");
typeList.add("Long ");
typeList.add("Integer ");
String[] names = ...;
List<String> nameList = ...;
for (Sting name : names) {
nameList.add(name);
}
}
public void demo() {
List<String> typeList = new ArrayList<>(8);
typeList.add("Short ");
typeList.add("Long ");
typeList.add("Integer ");
String[] names = ...;
List<String> nameList = ...;
nameList.addAll(Arrays.asList(names));
}
13. 直接迭代需要使用的集合
- 直接迭代需要使用的集合,无需通过其他操作获取数据
- 反例
public void demo() {
Map<Long, UserDO> userMap = ...;
for (Long userId : userMap.keySet()) {
UserDO user = userMap.get(userId);
}
}
public void demo() {
Map<Long, UserDO> userMap = ...;
for (Map.Entry<Long, UserDO> userEntry : userMap.entrySet()) {
Long userId = userEntry.getKey();
UserDO user = userEntry.getValue();
}
}
14. 不要使用size方法检测空,必须使用isEmpty方法测空
- 使用size方法上来检测空逻辑上没有问题,但使用isEmpty方法使得代码更易读,并且可以获得更好的性能。任何isEmpty方法实现的时间复杂度都是O(1),但是某些size方法实现的时间复杂度有可能是O(n)。
- 反例
public void demo() {
List<UserDO> userList = ...;
if (userList.size() == 0) {
...
}
Map<Long, UserDO> userMap = ...;
if (userMap.size() == 0) {
...
}
}
public void demo() {
List<UserDO> userList = ...;
if (userList.isEmpty()) {
...
}
Map<Long, UserDO> userMap = ...;
if (userMap.isEmpty()) {
...
}
}
15. 非随机访问的List,尽量使用迭代代替随机访问
public void demo() {
LinkedList<UserDO> userDOLinkedList = ...;
int size = userDOLinkedList.size();
for (int i = 0; i < size; i++) {
UserDO userDO = userDOLinkedList.get(i);
}
}
public void demo() {
LinkedList<UserDO> userDOLinkedList = ...;
int size = userDOLinkedList.size();
for (UserDO userDO : userDOLinkedList) {
...
}
}
16. 尽量使用HashSet判断值存在
- 在Java集合类库中,List的contains方法普遍时间复杂度是O(n),而HashSet的时间复杂度为O(1)。如果需要频繁调用contains方法查找数据,可以先将List转换成HashSet。
- 反例
public void demo() {
List<Long> adminList = ...;
List<UserDO> userDOList = ...;
List<UserVO> userVOList = new ArrayList<>(userDOList.size());
for (UserDO userDO : userDOList) {
if (adminList.contains(userDO.getId())) {
userVOList.add(transUser(userDO));
}
}
}
public void demo() {
Set<Long> adminList = ...;
List<UserDO> userDOList = ...;
List<UserVO> userVOList = new ArrayList<>(userDOList.size());
for (UserDO userDO : userDOList) {
if (adminList.contains(userDO.getId())) {
userVOList.add(transUser(userDO));
}
}
}
17. 避免先判断存在再进行获取
- 如果需要先判断存在再进行获取,可以直接获取并判空,从而避免了二次查找操作。
- 反例
public static UserVO transUser(UserDO user, Map<Long, RoleDO> roleMap) {
UserVO userVO = new UserVO();
userVO.setId(user.getId());
if (roleMap.containsKey(user.getRoleId)) {
userVO.setRole(transRole(role));
}
}
public static UserVO transUser(UserDO user, Map<Long, RoleDO> roleMap) {
UserVO userVO = new UserVO();
userVO.setId(user.getId());
RoleDO role = roleMap.get(user.getRoleId());
if (Objects.nonNull(role)) {
userVO.setRole(transRole(role));
}
}
18. 直接捕获对应的异常
- 直接捕获对应的异常,避免用 instanceof 判断,效率更高,代码更简洁。
- 反例
public void demo() {
try {
saveData();
} catch (Exception e) {
if (e instanceof IOException) {
log.error("保存数据IO异常 ", e);
} else {
log.error("保存数据其他异常 ", e);
}
}
}
public void demo() {
try {
saveData();
} catch (IOException e) {
log.error("保存数据IO异常 ", e);
} catch (Exception e) {
log.error("保存数据其他异常 ", e);
}
}
19. 尽量使用线程池减少线程开销
- 多线程中两个必要的开销:线程的创建和上下文切换。采用线程池,可以尽量避免这些开销。
21. 工具类中屏蔽构造函数
- 工具类是一堆静态字段和函数的集合,其不应该被实例化;但是,Java为每个没有明确定义构造函数的类添加了一个隐式公有构造函数,为了避免不必要的实例化,应该显示定义私有构造函数来屏蔽这个隐式公有构造函数。
22. 返回空数组和集合而非null
- 如果返回null,需要进行结果检查,否则报空异常;返回空数组或者空集合,有效避免了由于未检测null而抛出空指针的情况,还可以删除调用方检测null的语句使代码更简洁。
- 反例
public static List<Result> getResultList() {
return null;
}
public static List<Result> getResultList() {
return Collections.EMPTY_LIST;
}
23. 优先使用常量或者确定值调用equals方法
- 对象调用equals方法容易抛空指针,应该使用常量或者确定值来调用equals方法。
- 反例
private static boolean fileReader(String fileName) {
// 可能报空异常
return fileName.equals("Java开发手册 ");
}
private static boolean fileReader(String fileName) {
return "Java开发手册 ".equals(fileName);
}
24. 使用通用工具函数Objects.equals()
thisName != null && thisName.equals(name)
thisName == name || (thisName != null && thisName.equals(name))
Objects.equals(name, thisName)
25. 使用通用工具函数CollectionUtil.isEmpty()
list != null && list.isEmpty()
CollectionUtils.isNotEmpty(list)