如何写出一手漂亮的代码?

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,尽量使用迭代代替随机访问

  • 通过get获取非随机访问数据效率极低
  • 反例
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)
发布了117 篇原创文章 · 获赞 309 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_36221788/article/details/105565378