16 个 Java 代码“痛点”大改造:“一般写法” VS “高级写法”终极对决,看完代码质量飙升!

引言

这篇文章集合了一些我在日常 Java 开发中真实遇到的痛点 —— 以及我是如何将它们重写得更智能、更精炼、更整洁的。不玩什么花里胡 secretário的技巧,就是实实在在地把代码写得更好。


1. 手动进行对象映射

  • • 一般写法 :
    // 从 User 实体手动映射到 UserDTO
    UserDTO dto = new UserDTO();
    dto.setId(user.getId());
    dto.setEmail(user.getEmail());
    dto.setActive(user.isActive());
    dto.setRole(user.getRole().name()); // 假设 Role 是枚举
  • • 高级写法:
    import org.mapstruct.Mapper;
    
    @Mapper(componentModel = "spring") // 声明为 MapStruct Mapper,并集成 Spring
    public interface UserMapper {
        UserDTO toDto(User user); // 定义映射方法
        // MapStruct 会在编译时自动生成这个接口的实现
    }
    // 使用时: UserDTO dto = userMapper.toDto(user);
  • • 为啥这样更高级? 你只需要编写接口,MapStruct 会在编译时为你生成具体的实现代码。这意味着零运行时开销不需要反射,也告别了手动映射可能带来的维护地狱

    • • 拓展阅读:什么是 MapStruct?为何每个 Java 开发者都在谈论它


**2. 链式空指针检查和条件判断 **

  • • 一般写法 :
    // 一层又一层的 null 检查,非常丑陋且容易出错
    if (order != null && order.getCustomer() != null && order.getCustomer().getEmail() != null) {
        sendEmail(order.getCustomer().getEmail());
    }
  • • 高级写法 :
    import java.util.Optional;
    
    // 使用 Optional 进行链式调用,优雅处理潜在的 null 值
    Optional.ofNullable(order) // order 可能为 null
        .map(Order::getCustomer) // 如果 order 不为 null,则获取 customer
        .map(Customer::getEmail) // 如果 customer 不为 null,则获取 email
        .ifPresent(this::sendEmail); // 如果 email 不为 null,则执行 sendEmail
  • • 为啥这样更高级? 再也不会掉进 NullPointerException 的坑了!代码可读性强,而且即使链条中的某个环节是 null,程序也不会直接崩溃。


**3. 编写“傻瓜式”循环 **

  • • 一般写法 :
    // 手动遍历列表并将每个名字转换为大写
    List<String> upper = new ArrayList<>();
    for (String name : names) {
        upper.add(name.toUpperCase());
    }
  • • 高级写法:
    // 使用 Stream API 进行函数式转换
    List<String> upper = names.stream()
                              .map(String::toUpperCase) // 映射操作:每个元素转大写
                              .toList(); // 收集结果为 List (Java 16+)
                              // .collect(Collectors.toList()); // Java 8 - 15 写法
  • • 为啥这样更高级? 代码更少,表达力更强。函数式风格更容易理解其背后的逻辑 —— 而且 Stream 的转换操作可以轻松地进行链式调用


4. 手动构建 JSON 响应

  • • 一般写法 :
    // 手动用 Map 构建 JSON 结构
    Map<String, Object> map = new HashMap<>();
    map.put("id", user.getId());
    map.put("email", user.getEmail());
    map.put("active", user.isActive());
    // 如果字段很多,这里会非常冗长
    return new ResponseEntity<>(map, HttpStatus.OK);
  • • 高级写法 :
    // 直接返回 DTO 对象,让 Jackson (Spring Boot 默认的 JSON 库) 自动序列化
    return ResponseEntity.ok(new UserDTO(user)); // 假设 UserDTO 有合适的构造函数或用 UserMapper 转换
    或者,如果 Controller 方法的返回类型就是 UserDTO,可以直接返回 DTO 实例,Spring MVC 和 Jackson 会帮你处理剩下的序列化工作。
  • • 为啥这样更高级? 你很可能已经在项目中间接或直接地使用了 Jackson。让它去干这些序列化和反序列化的脏活累活吧,别自己费劲了。


**5. 充满 Getter 和 Setter 的样板式 DTO **

  • • 一般写法 :
    public class ProductDTO {
        private String name;
        private BigDecimal price;
    
        // 大量的 Getter 和 Setter 方法...
        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
        public BigDecimal getPrice() { return price; }
        public void setPrice(BigDecimal price) { this.price = price; }
        // 可能还有 equals(), hashCode(), toString()...
    }
  • • 高级写法 :
    // 使用 record 定义不可变的数据传输对象
    public record ProductDTO(String name, BigDecimal price) {}
    // 编译器会自动生成构造函数、getter、equals()、hashCode() 和 toString()
  • • 为啥这样更高级? Java record 非常适合用作不可变的数据传输对象。代码更简洁、更安全,而且完全没有样板代码的烦恼。


**6. 过度使用 if-else 和 switch 语句 **

  • • 一般写法 :
    // 一长串的 if-else if
    if ("ADMIN".equals(role)) {
        return "拥有所有权限";
    } else if ("USER".equals(role)) {
        return "拥有有限权限";
    } else {
        return "无权限";
    }
  • • 高级写法 :
    // 使用 switch 表达式,更简洁、更安全
    return switch (role) {
        case "ADMIN" -> "拥有所有权限";
        case "USER" -> "拥有有限权限";
        default -> "无权限";
    }; // 注意这里是表达式,可以返回值,末尾可能有分号
  • • 为啥这样更高级? switch 表达式更简洁、更不容易出错(比如忘记 break 导致的穿透问题),并且不需要 break 语句。


**7. 仅仅为了关闭资源而编写 try-catch-finally **

  • • 一般写法 :
    Connection conn = null;
    Statement stmt = null;
    try {
        conn = dataSource.getConnection();
        stmt = conn.createStatement();
        // ... 使用 conn 和 stmt ...
    } catch (SQLException e) {
        // 处理异常
    } finally {
        // 繁琐的资源关闭逻辑,还可能嵌套 try-catch
        if (stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) { /* log or ignore */ }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) { /* log or ignore */ }
        }
    }
  • • 高级写法 :
    // 使用 try-with-resources 语句,资源会自动关闭
    try (Connection conn = dataSource.getConnection();
         Statement stmt = conn.createStatement()) { // 实现了 AutoCloseable 接口的资源都可以放这里
        // ... 使用 conn 和 stmt ...
    } catch (SQLException e) {
        // 处理异常
    }
    // conn 和 stmt 会在这里被自动关闭,即使发生异常
  • • 为啥这样更高级? Try-with-resources 结构能自动处理资源的清理工作,即使在发生异常的情况下也能保证资源被正确关闭,代码也更简洁。


8. 重复发明工具函数

  • • 一般写法 :
    // 自己写一个判断字符串是否为 null 或空的工具方法
    public static boolean isNullOrEmpty(String s) {
        return s == null || s.trim().isEmpty();
    }
  • • 高级写法:
    // 使用 Apache Commons Lang 库中的 StringUtils.isBlank()
    // 它能处理 null、空字符串以及只包含空白字符的情况
    boolean isEmpty = StringUtils.isBlank(s);
    // 或者 Guava 的 Strings.isNullOrEmpty(s)
  • • 为啥这样更高级? 多多利用那些稳定、开源的库(如 Apache Commons, Guava)。停止编写那些已经被成千上万次编写和测试过的代码,不要重复造轮子


**9. 自己动手实现对象缓存 **

  • • 一般写法:
    // 手动用 HashMap 实现简单的缓存
    Map<String, User> userCache = new HashMap<>();
    
    public User getUserById(String id) {
        if (userCache.containsKey(id)) {
            return userCache.get(id);
        } else {
            User user = fetchUserFromDatabase(id); // 假设这是从数据库获取用户的方法
            if (user != null) {
                userCache.put(id, user);
            }
            return user;
        }
    }
  • • 高级写法 :
    // 使用 Java 8 Map 的 computeIfAbsent 方法,更简洁且线程安全 (对于 ConcurrentMap)
    // private ConcurrentMap<String, User> userCache = new ConcurrentHashMap<>();
    // return userCache.computeIfAbsent(id, key -> fetchUserFromDatabase(key));
    // 或者使用更专业的缓存库如 Caffeine / Guava Cache
    // LoadingCache<String, User> userCache = Caffeine.newBuilder()
    //    .maximumSize(100)
    //    .expireAfterWrite(10, TimeUnit.MINUTES)
    //    .build(key -> fetchUserFromDatabase(key));
    // return userCache.get(id);
    对于原文示例,如果 cache 是 ConcurrentMap
    // 假设 cache 是一个 ConcurrentMap<String, User>
    return cache.computeIfAbsent(id, this::fetchUser); // 或者 key -> fetchUser(key)
  • • 为啥这样更高级? computeIfAbsent 更简洁,并且对于 ConcurrentMap 来说是原子操作,因此线程安全。而使用像 Caffeine 或 Guava Cache 这样的专业缓存库,你甚至不用操心缓存大小、过期策略、淘汰策略等复杂问题。


很高兴你觉得这些内容有用!让我们继续探讨更多真实场景下的智能 Java 代码片段 —— 这些例子几乎在每个项目中都会遇到,但往往被用“一般写法”编写。这些例子都源于实际应用,而非空谈理论。


10. 手动解析和格式化日期

  • • 一般写法 :
    // SimpleDateFormat 是可变的,且线程不安全
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    try {
        Date date = sdf.parse(inputString); // 解析字符串为 Date
        String outputString = sdf.format(date); // 格式化 Date为字符串
    } catch (ParseException e) {
        e.printStackTrace();
    }
  • • 高级写法:
    import java.time.LocalDate;
    import java.time.format.DateTimeFormatter;
    
    // 解析字符串 (假设 input 是 "yyyy-MM-dd" 格式,这是 LocalDate 的默认格式)
    LocalDate date = LocalDate.parse(inputString);
    // 格式化为字符串 (默认也是 "yyyy-MM-dd")
    String output = date.toString();
    
    // 或者使用自定义格式
    DateTimeFormatter customFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
    String formattedDate = LocalDate.now().format(customFormatter);
  • • 为啥这样更高级? 旧的 java.util.Date 和 SimpleDateFormat 类是可变的、易出错,并且不是线程安全的。而 java.time 包(如 LocalDateLocalDateTimeDateTimeFormatter)是现代、整洁、安全且线程安全的选择。


**11. 手动过滤和排序列表 **

  • • 一般写法 :
    // 手动循环过滤
    List<User> activeUsers = new ArrayList<>();
    for (User user : allUsers) {
        if (user.isActive()) {
            activeUsers.add(user);
        }
    }
    // 手动排序
    Collections.sort(activeUsers, new Comparator<User>() {
        @Override
        public int compare(User u1, User u2) {
            return u1.getName().compareTo(u2.getName());
        }
    });
  • • 高级写法:
    import java.util.Comparator;
    import java.util.stream.Collectors;
    
    // 使用 Stream API 进行链式操作
    List<User> result = allUsers.stream()
        .filter(User::isActive) // 过滤:只保留 active 的用户
        .sorted(Comparator.comparing(User::getName)) // 排序:按名字排序
        .toList(); // 收集结果 (Java 16+)
        // .collect(Collectors.toList()); // Java 8 - 15
  • • 为啥这样更高级? Stream API 使得数据转换操作更具可读性且易于组合 —— 特别是当你需要链式地进行多个过滤(filter)、映射(map)和收集(collect)操作时。


12. 仅仅为了静态常量而编写嵌套类 (Writing Nested Classes Just for Static Constants)

  • • 一般写法 (The Normal Way):
    // 用一个类专门存放常量字符串
    public class AppConstants {
        public static final String ROLE_ADMIN = "ADMIN";
        public static final String ROLE_USER = "USER";
        // ... 可能还有更多
    }
    // 使用时:if (userRole.equals(AppConstants.ROLE_ADMIN))
  • • 高级写法 (The Advanced Way - 使用枚举 enum 或 Java 17+ 的 sealed interface + record):

    • • 使用枚举 (Enum - 通常是最佳选择):
      public enum Role {
          ADMIN, USER // 枚举实例本身就是类型安全的常量
      }
      // 使用时:if (userRole == Role.ADMIN)
    • • 使用 sealed interface 和 record (Java 17+,适用于更复杂的常量结构):
      // 定义一个密封接口
      public sealed interface Role permits Role.Admin, Role.User {
          // 定义实现了该接口的 record 作为具体常量类型
          record Admin() implements Role {}
          record User() implements Role {}
      }
      // 使用时:if (userRole instanceof Role.Admin)
  • • 为啥这样更高级? 枚举 (Enum) 提供了类型安全(你不能把一个不相关的字符串赋给 Role 类型的变量)、清晰的序列化行为,并且能很好地配合现代 switch 表达式使用。普通的字符串常量可没这些好处。sealed interface + record 提供了更强的模式匹配能力。


13. 手动对数字列表求和或求平均值 (Summing or Averaging a List of Numbers)

  • • 一般写法 (The Normal Way):
    // 手动循环累加
    int totalAmount = 0;
    for (Order order : orders) {
        totalAmount += order.getAmount(); // 假设 getAmount() 返回 int
    }
  • • 高级写法 (The Advanced Way - 使用 Stream API):
    // 使用 Stream API 的 sum() 方法
    int total = orders.stream()
        .mapToInt(Order::getAmount) // 将 Order 流映射为 IntStream
        .sum(); //直接求和
    
    // 或者求平均值
    double averageAmount = orders.stream()
        .mapToDouble(Order::getAmount) // 映射为 DoubleStream
        .average() // 计算平均值,返回 OptionalDouble
        .orElse(0.0); // 如果列表为空,则返回 0.0
  • • 为啥这样更高级? 不需要显式的累加变量,减少了可变状态,代码更简洁。Stream API 提供了内置的聚合函数,如 sum()average()count()max()min() 等。


14. 用策略模式或多态替换嵌套的 If 语句 (Replacing Nested Ifs With Strategy or Polymorphism)

  • • 一般写法 (The Normal Way):
    // 一长串的 if-else if 根据类型执行不同操作
    if ("paypal".equals(paymentType)) {
        processPaypalPayment();
    } else if ("credit_card".equals(paymentType)) {
        processCreditCardPayment();
    } else if ("bank_transfer".equals(paymentType)) {
        processBankTransferPayment();
    } else {
        // 默认处理或报错
    }
  • • 高级写法 (The Advanced Way - 使用 Map 存储策略实现):
    // 假设 PaymentStrategy 是一个接口,PaypalStrategy 等是其实现类
    // interface PaymentStrategy { void process(); }
    // class PaypalStrategy implements PaymentStrategy { @Override public void process() {/* ... */} }
    // ...
    
    // 使用 Map 来存储和分发策略
    Map<String, PaymentStrategy> paymentStrategies = Map.of(
        "paypal", new PaypalStrategy(),
        "credit_card", new CreditCardStrategy(),
        "bank_transfer", new BankTransferStrategy()
    );
    
    // 获取并执行策略,如果找不到则使用默认策略
    PaymentStrategy strategy = paymentStrategies.getOrDefault(paymentType, new DefaultPaymentStrategy());
    strategy.process();
    (这实际上是策略模式的一种简单实现。在 Spring 中,通常会将策略实现为 Bean,并通过 Bean Name 或其他标识从应用上下文中获取。)
  • • 为啥这样更高级? 避免了冗长的 if-else 或 switch 代码块。添加新的支付策略时,通常只需要增加一个新的策略实现类并注册到 Map(或 Spring 容器)中,而不需要修改现有的分发逻辑(符合开闭原则)。


15. 将 JSON 反序列化为对象 (Deserializing JSON to Objects)

  • • 一般写法 (The Normal Way):
    // ObjectMapper mapper = new ObjectMapper(); // 每次都 new 一个?或者到处注入?
    // User user = mapper.readValue(jsonString, User.class);
    这种写法本身没大问题,但问题在于,如果项目中到处都这样写,并且每个地方都单独处理 IOException,代码会变得重复且难以管理。
  • • 高级写法 (The Advanced Way - 封装成工具方法):
    首先,封装一次:
    import com.fasterxml.jackson.databind.ObjectMapper;
    import java.io.IOException;
    
    public class JsonUtil {
        // ObjectMapper 实例是线程安全的,可以静态共享
        private static final ObjectMapper MAPPER = new ObjectMapper();
        // 可以在这里对 MAPPER 进行全局配置,例如日期格式、忽略未知属性等
    
        public static <T> T fromJson(String json, Class<T> clazz) {
            try {
                return MAPPER.readValue(json, clazz);
            } catch (IOException e) {
                // 将受检异常转换为运行时异常,或者抛出自定义的业务异常
                throw new RuntimeException("JSON 解析错误: " + e.getMessage(), e);
            }
        }
    
        public static String toJson(Object object) {
            try {
                return MAPPER.writeValueAsString(object);
            } catch (IOException e) {
                 throw new RuntimeException("JSON 序列化错误: " + e.getMessage(), e);
            }
        }
    }
    然后复用这个工具方法:
    // User user = JsonUtil.fromJson(jsonString, User.class);
  • • 为啥这样更高级? 将 JSON (反)序列化逻辑、错误处理以及 ObjectMapper 的配置集中管理。代码更符合 DRY (Don't Repeat Yourself) 原则,也更整洁。


16. 避免冗余的线程管理 (Avoiding Verbose Thread Management)

  • • 一般写法 (The Normal Way - 手动创建和管理线程池):
    // ExecutorService executor = Executors.newFixedThreadPool(10);
    // executor.submit(() -> {
    //     // 执行耗时任务...
    //     doWork();
    // });
    // // 还需要考虑 executor 的关闭等问题
  • • 高级写法 (The Advanced Way - 在 Spring Boot 中使用 @Async 或 CompletableFuture):

    • • 使用 Spring Boot 的 @Async (需要在配置类启用 @EnableAsync):
      import org.springframework.scheduling.annotation.Async;
      import org.springframework.stereotype.Service;
      import java.util.concurrent.CompletableFuture;
      
      
      @Service
      public class MyAsyncService {
          @Async // 标记此方法为异步执行 (由 Spring 管理的线程池执行)
          public CompletableFuture<String> doWorkAsync() {
              // 执行耗时任务...
              try { Thread.sleep(1000); } catch (InterruptedException e) {}
              return CompletableFuture.completedFuture("异步任务完成");
          }
      }
    • • 或者直接使用 CompletableFuture (Java 8+):
      // CompletableFuture.runAsync(() -> doWork()); // 如果没有返回值
      // CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
      //     // 执行耗时任务并返回结果...
      //     return "结果";
      // });
  • • 为啥这样更高级? Spring Boot 的 @Async 提供了声明式的异步处理,能利用其管理的内置线程池,通常比手动管理 ExecutorService 更简单、更易于配置,也更利于扩展。CompletableFuture 则是 Java 现代并发编程的强大工具。


✨ 总结:更高级的 Java 写法,其实就是现代 Java 的标准写法

这些并非什么“奇技淫巧”,它们是现代 Java 开发中**“更好的默认做法”**。

Java 已经进化良多 —— 如果你还固守着 5-10 年前学习 Java 时的方式,那你很可能写了太多不必要的代码,干了太多不必要的活儿,效率也大打折扣。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

java干货

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值