顾乔芝士网

持续更新的前后端开发技术栈

Java屎山代码之坏味道分析(三)

以下是个人总结的一些代码习惯问题和优化第三期,单独一个也许不起眼,但堆积起来,就让项目代码变成一座屎山。



避免函数过度封装

o 避免函数过度封装o 严重性:高。函数过度封装会增加方法调用的开销,降低程序的性能,并使代码变得复杂,降低代码的可读性。

o 避免方法:函数应该言简意赅,表达清楚意思即可,避免不必要的复杂封装。


举个正例:

public void complexFunction() {
    if (condition1) {
        handleCondition1();
    } else if (condition2) {
        handleCondition2();
    }
    // 其他逻辑...
}

private void handleCondition1() {
    // 处理条件1的逻辑
}

private void handleCondition2() {
    // 处理条件2的逻辑
}

举个反例:

public void complexFunction() {
    // 一系列复杂的逻辑...
    if (condition1) {
        // 逻辑1
    } else if (condition2) {
        // 逻辑2
    }
    // 更多逻辑...
}

变量初值处理

o 变量初值处理o 严重性:中。如果变量的初值一定会被覆盖,给变量赋初值是没有必要的,会浪费内存和时间。o 避免方法:正确处理变量的初值,仅在必要时初始化变量,以提高程序的性能。


举个正例:

for (int i = 0; i < array.length; i++) {
    // 直接使用i,无需初始化count
}

举个反例:

int count = 0;
for (int i = 0; i < array.length; i++) {
    count = i; // 每次循环都会覆盖count的值
}

金额数值计算用BigDecimal

o 严重性:高。由于计算机是以二进制存储数值的,对于浮点数的计算可能会导致精确度缺失。o 避免方法:在财务系统中,对于金额的计算应该使用BigDecimal(或者使用分为单位进行处理),以确保计算结果的准确性。


举个正例:

BigDecimal amount = new BigDecimal("100.0").divide(new BigDecimal("3.0"), 2, RoundingMode.HALF_UP);

举个反例:

double amount = 100.0 / 3.0; // 会导致精度丢失

注意Arrays.asList的坑

o 注意Arrays.asList的坑o 严重性:中。Arrays.asList方法在使用时有一些需要注意的地方,如基本类型不能作为参数、返回的列表不支持增删操作等。o 避免方法:了解Arrays.asList的限制,选择合适的方式将数组转换为列表,避免程序出现异常。


举个正例:

List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
list.add("d"); // 正常工作,因为ArrayList支持增删操作

举个反例:

List<String> list = Arrays.asList("a", "b", "c");
list.add("d"); // 运行时异常,Arrays.asList返回的列表不支持增删操作

优先使用基本类型临时变量

o 优先使用基本类型临时变量o 严重性:中。基本类型参数以及临时变量保存在栈中,访问速度比较快,而对象类型的访问速度较慢。o 避免方法:在方法函数内,优先使用基本类型临时变量以提高程序的性能。


举个正例:

int tmp = someIntValue + 10; // 使用基本类型

举个反例:

Integer tmp = someIntValue + 10; // 使用包装类

数据库查询分页处理

o 数据库查询分页处理o 严重性:高。如果数据库一次查询的数量过多,可能会导致性能问题,如查询时间过长、内存占用过高。o 避免方法:分页处理可以将大量数据分成小块进行查询,提高查询效率,减少资源消耗。


举个正例:

Pageable pageable = PageRequest.of(page, size, Sort.by("id"));
List<Item> items = itemDao.findAll(pageable); // 分页查询

举个反例:

List<Item> items = itemDao.findAll(); // 查询所有数据

减少变量重复计算

o 严重性:中。对方法的调用,即使是只有一个语句,也会有一定的消耗。o 避免方法:在一些情况下,减少变量的重复计算可以提高程序的性能,例如在循环中缓存列表长度。


举个正例:

int size = list.size();
for (int i = 0; i < size; i++) {
    // 先计算size,避免在循环中重复计算
}

举个反例:

for (int i = 0; i < list.size(); i++) {
    // 每次循环都调用list.size(),可能导致不必要的计算
}

考虑接口兼容性

o 严重性:高。在修改对外老接口时,如果不考虑接口兼容性,可能会导致旧版本的客户端无法正常使用接口。o 避免方法:修改接口时考虑向后兼容性,可以采用默认值、可选参数等方式进行处理。


举个正例:

public void oldMethod(String param, String newParam = null) {
    // 新方法实现,兼容旧客户端
}

举个反例:

public void oldMethod(String param) {
    // 旧方法实现
}
//不向后兼容,直接修改
public void oldMethod(String param, String newParam) {
    // 新方法实现,旧客户端可能无法调用
}

避免运行时错误

o 严重性:高。运行时错误如数组边界溢出、被零除等,可能会导致程序崩溃或产生不可预期的结果。o 避免方法:采取措施避免运行时错误,例如检查数组长度和除数是否为零。

举个正例:

public int divide(int a, int b) {
    if (b == 0) {
        throw new IllegalArgumentException("Divider cannot be zero.");
    }
    return a / b;
}

举个反例:

public int divide(int a, int b) {
    return a / b; // 可能的被零除错误
}

注意ArrayList.toArray()强转问题

o 严重性:中。ArrayList.toArray()方法返回的是Object类型的数组,直接强转为特定类型的数组可能会发生ClassCastException。o 避免方法:使用toArray(T[]a)的重载方法,避免类型转换错误。


举个正例:

List<String> list = new ArrayList<>();
String[] array = list.toArray(new String[0]); // 安全的类型转换

举个反例:

List<String> list = new ArrayList<>();
String[] array = (String[]) list.toArray(); // 可能的ClassCastException

避免循环内远程或数据库操作

o 严重性:高。远程操作或数据库操作通常比较耗时和耗资源,在循环内进行这些操作会导致性能问题。o 避免方法:批量进行这些操作可以提高效率,减少资源消耗。


举个正例:

List<User> users = database.getAllUsers(); // 批量获取所有用户
for (User user : users) {
    // 处理每个用户
}

举个反例:

for (int i = 0; i < users.size(); i++) {
    User user = database.getUserById(i); // 每次循环都进行数据库操作
}

避免大数据量一次性读入内存

o 严重性:高。一次性将大文件或大量数据从数据库读入内存,可能会导致内存溢出问题。o 避免方法:分批处理数据可以避免这个问题,提高程序的稳定性。


举个正例:

Stream<String> lines = Files.lines(Paths.get("largefile.txt")); // 逐行处理,不一次性读入内存
lines.forEach(line -> {
    // 处理每一行
});

举个反例:

List<String> allLines = Files.readAllLines(Paths.get("largefile.txt")); // 可能会耗尽内存

调用第三方接口的注意事项

o 严重性:高。调用第三方接口时,需要考虑异常处理、安全性、超时重试等问题。o 避免方法:设置合理的超时时间,确保传递给第三方接口的数据是经过加密或安全处理的,合理设置重试次数和间隔时间。


举个正例:

public String callThirdPartyService(String data) {
    try {
        return thirdPartyService.send(data, 5, TimeUnit.SECONDS); // 设置超时时间
    } catch (TimeoutException e) {
        // 处理超时
    } catch (Exception e) {
        // 处理其他异常
    }
    return null;
}

举个反例:

public String callThirdPartyService(String data) {
    String response = thirdPartyService.send(data); // 没有处理异常或超时
    return response;
}

使用JDK方法拷贝集合

o 严重性:中。使用JDK提供的方法拷贝集合可以避免手动循环拷贝带来的性能问题和错误风险。o 避免方法:使用JDK提供的addAll等方法进行集合拷贝,确保拷贝的准确性和高效性。


举个正例:

List<Integer> original = new ArrayList<>(Arrays.asList(1, 2, 3));
List<Integer> copy = new ArrayList<>(original); // 使用构造函数进行拷贝

举个反例:

List<Integer> original = new ArrayList<>(Arrays.asList(1, 2, 3));
List<Integer> copy = new ArrayList<>();
for (Integer item : original) {
    copy.add(item);
}

多线程考虑线性安全

o 多线程考虑线性安全o 严重性:高。在多线程环境下,不同的集合类具有不同的线程安全性,使用线性不安全的集合类可能会导致数据不一致、程序崩溃等问题。o 避免方法:在多线程的应用中,选择线性安全的集合类,如ConcurrentHashMap、Vector等。


举个正例:

List<Integer> list = Collections.synchronizedList(new ArrayList<>()); // 线程安全包装

举个反例:

List<Integer> list = new ArrayList<>(); // 非线程安全

注意spring事务失效的坑

o 严重性:高。在使用Spring事务功能时,了解事务失效的常见原因可以确保事务的正确使用,保证数据的一致性和完整性。o 避免方法:确保事务注解正确使用,避免底层数据库引擎不支持事务、方法不是public修饰的、rollbackFor属性设置错误等情况。

举个正例:

@Transactional
public void transferMoney(@Param("from") String from, @Param("to") String to) {
    // 转账逻辑,正确使用@Transactional注解
}

举个反例:

@Transactional
private void transferMoney(@Param("from") String from, @Param("to") String to) {
    // 转账逻辑,@Transactional注解可能因为方法不是public而失效
}

Executors声明线程池的OOM问题

o 严重性:

高。使用
Executors.newFixedThreadPool声明线程池时,如果不注意其使用的无界队列可能会导致内存溢出问题。o 避免方法:使用有界队列或者自定义线程池,合理设置线程数量和队列大小,以避免这个问题。


举个正例:

ExecutorService executor = new ThreadPoolExecutor(
    10, 10,
    0L, TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<Runnable>(100)); // 有界队列

举个反例:

ExecutorService executor = Executors.newFixedThreadPool(10); // 无界队列可能导致OOM

接口考虑幂等性

o 严重性:高。接口的幂等性对于一些重要的业务场景非常关键,如抢红包、转账等。o 避免方法:通过查询操作、唯一索引、token机制、数据库的delete/update操作、乐观锁、悲观锁、分布式锁等技术方案来实现接口的幂等性。


举个正例:

public void sendMoney(String userId, BigDecimal amount) {
    if (!hasSent(userId)) {
        // 发送金额逻辑
        markSent(userId);
    }
}

举个反例:

public void sendMoney(String userId, BigDecimal amount) {
    // 发送金额逻辑,没有检查是否重复发送
}

划分长函数为小函数

o 严重性:中。一个过于冗长的函数难以理解和维护。o 避免方法:将长函数划分成小函数,每个小函数专注于一个特定的功能,使得代码结构更加清晰。建议每个函数不超过80行!

举个正例:

public void processOrder(Order order) {
    validateOrder(order);
    calculatePrice(order);
    updateInventory(order);
    sendOrder(order);
}

private void validateOrder(Order order) {
    // 验证订单
}

private void calculatePrice(Order order) {
    // 计算价格
}

private void updateInventory(Order order) {
    // 更新库存
}

private void sendOrder(Order order) {
    // 发送订单
}

举个反例:

public void processOrder(Order order) {
    // 验证订单
    // 计算价格
    // 更新库存
    // 发送订单
    // 更多逻辑...
}

关键业务代码打印日志

o 严重性:中。关键业务代码打印足够的日志可以帮助开发人员在出现问题时快速定位问题所在。o 避免方法:在关键业务代码中打印足够的日志信息,正确使用日志级别,避免不必要的告警和干扰。

举个正例:

public void executeCriticalTask() {
    try {
        // 执行关键任务
    } catch (Exception e) {
        log.error("Failed to execute critical task", e);
    }
}

举个反例:

public void executeCriticalTask() {
    try {
        // 执行关键任务
    } catch (Exception e) {
        // 无日志记录
    }
}

可变因素配置化

o 严重性:中。将某些可变因素做成配置化可以提高系统的灵活性和可维护性。o 避免方法:将可变因素设置为配置项,当这些因素发生变化时,只需要修改配置文件,而不需要修改代码和重新发布系统。

举个正例:

@Configuration
public class AppConfig {
    @Value("${threshold.value}")
    private int threshold;

    public void process() {
        // 使用配置值处理逻辑
    }
}

举个反例:

public void process() {
    int threshold = 100; // 硬编码的阈值
    // 处理逻辑
}

直接迭代集合

o 严重性:中。直接迭代需要使用的集合可以避免不必要的操作,提高代码的效率和可读性。o 避免方法:选择合适的迭代方式,例如在遍历Map集合时,直接使用entrySet进行迭代可以同时获取键和值。


举个正例:

Map<Integer, String> map = new HashMap<>();
for (Map.Entry<Integer, String> entry : map.entrySet()) {
    Integer key = entry.getKey();
    String value = entry.getValue();
}

举个反例:

Map<Integer, String> map = new HashMap<>();
for (Integer key : map.keySet()) {
    String value = map.get(key);
}



以上内容请参考,喜欢的请点个赞哦!


ヾ(@^▽^@)ノ

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言