mcpField
J
java-best-practices

java-best-practices

github.com/clostaunau开发1188 次安装4 周前更新
代码仓库

关于 java-best-practices

面向 Java 开发者的实践参考,涵盖代码审查、编写与重构中的设计原则和常见问题。内容涉及 SOLID、Clean Code、测试策略及 Spring Framework。

主要功能

  1. 01SOLID 与 DRY 原则
  2. 02Clean Code 编写建议
  3. 03Java 8+ 特性用法
  4. 04测试与可测试性指导
  5. 05Spring Framework 实践

适用场景

  1. 01审查 Java 项目代码
  2. 02编写或重构 Java 代码
  3. 03评估架构与设计决策

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/clostaunau/holiday-card --skill java-best-practices

安装后在 Claude Code 中输入/java-best-practices确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

templates/
builder-pattern.java
README.md
service-template.java
test-template.java

详细介绍

Java 最佳实践

目的

本技能提供全面的 Java 开发最佳实践,可在代码审查和架构决策过程中作为参考指南。内容涵盖 SOLID 原则、DRY、Clean Code、Java 特定模式、测试策略和常见反模式。

使用本技能的场景:

  • 对 Java 项目进行代码审查
  • 编写新的 Java 代码
  • 重构现有 Java 代码
  • 评估架构和设计决策
  • 向团队成员讲解 Java 最佳实践
  • 使用 Spring Framework 应用

背景

高质量的 Java 代码对于构建易于维护、可扩展且稳健的应用至关重要。本技能记录了行业标准实践,重点包括:

  • SOLID 原则:设计良好的面向对象代码的基础
  • Clean Code:可读、易于维护且自说明的代码
  • Java 特性:恰当使用现代 Java 特性(8+)
  • 可测试性:易于测试和验证的代码
  • 性能:高效使用 Java 语言和 JVM 特性
  • Spring Framework:基于 Spring 的应用的最佳实践

本技能供 uncle-duke-java Agent 在代码审查期间参考,也供开发者在编写 Java 代码时使用。

前置条件

必备知识:

  • Java 基础知识(Java 8+)
  • 面向对象编程概念
  • 设计模式基础知识
  • 熟悉 Spring Framework(适用于 Spring 特定章节)

必备工具:

  • JDK 8 或更高版本(推荐 11、17 或 21)
  • 用于构建管理的 Maven 或 Gradle
  • 用于测试的 JUnit 5
  • 用于模拟的 Mockito
  • 支持 Java 的 IDE(IntelliJ IDEA、Eclipse、VS Code)

预期的项目结构:

project/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/example/
│   │   │       ├── model/
│   │   │       ├── service/
│   │   │       ├── repository/
│   │   │       ├── controller/
│   │   │       └── util/
│   │   └── resources/
│   │       └── application.properties
│   └── test/
│       └── java/
│           └── com/example/
├── pom.xml (or build.gradle)
└── README.md

Java 中的 SOLID 原则

单一职责原则(SRP)

规则: 一个类应该只有一个变更理由。每个类都应该有一个明确且定义清晰的职责。

重要性: 职责过多的类更难理解、测试和维护。对某一项职责的更改可能会影响其他职责。

实践中的 SRP

❌ 不佳 - 职责过多:

// This class violates SRP: it handles user data, validation, persistence, and email
public class User {
    private String email;
    private String password;

    // Responsibility 1: Data validation
    public boolean isValid() {
        return email != null && email.contains("@")
            && password != null && password.length() >= 8;
    }

    // Responsibility 2: Database operations
    public void save() {
        Connection conn = DriverManager.getConnection("jdbc:...");
        PreparedStatement ps = conn.prepareStatement("INSERT INTO users...");
        ps.setString(1, email);
        ps.setString(2, password);
        ps.executeUpdate();
    }

    // Responsibility 3: Email operations
    public void sendWelcomeEmail() {
        EmailService.send(email, "Welcome!", "Welcome to our app");
    }

    // Responsibility 4: Password encryption
    public void encryptPassword() {
        this.password = BCrypt.hashpw(password, BCrypt.gensalt());
    }
}

问题:

  • User 类有 4 项职责:数据、验证、持久化、电子邮件
  • 验证逻辑的变更会影响 User 类
  • 数据库架构的变更会影响 User 类
  • 电子邮件模板的变更会影响 User 类
  • 难以单独测试各项职责

✅ 良好 - 单一职责:

// Responsibility: Hold user data
public class User {
    private final String email;
    private final String passwordHash;

    public User(String email, String passwordHash) {
        this.email = email;
        this.passwordHash = passwordHash;
    }

    public String getEmail() { return email; }
    public String getPasswordHash() { return passwordHash; }
}

// Responsibility: Validate user data
public class UserValidator {
    public ValidationResult validate(String email, String password) {
        List<String> errors = new ArrayList<>();

        if (email == null || !email.contains("@")) {
            errors.add("Invalid email format");
        }
        if (password == null || password.length() < 8) {
            errors.add("Password must be at least 8 characters");
        }

        return new ValidationResult(errors.isEmpty(), errors);
    }
}

// Responsibility: Persist user data
public class UserRepository {
    private final DataSource dataSource;

    public UserRepository(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public void save(User user) {
        try (Connection conn = dataSource.getConnection();
             PreparedStatement ps = conn.prepareStatement(
                 "INSERT INTO users (email, password_hash) VALUES (?, ?)")) {
            ps.setString(1, user.getEmail());
            ps.setString(2, user.getPasswordHash());
            ps.executeUpdate();
        } catch (SQLException e) {
            throw new DataAccessException("Failed to save user", e);
        }
    }
}

// Responsibility: Send emails
public class EmailService {
    public void sendWelcomeEmail(User user) {
        send(user.getEmail(), "Welcome!", "Welcome to our app");
    }

    private void send(String to, String subject, String body) {
        // Email sending logic
    }
}

// Responsibility: Hash passwords
public class PasswordEncoder {
    public String encode(String rawPassword) {
        return BCrypt.hashpw(rawPassword, BCrypt.gensalt());
    }

    public boolean matches(String rawPassword, String encodedPassword) {
        return BCrypt.checkpw(rawPassword, encodedPassword);
    }
}

优点:

  • 每个类都有一项明确的职责
  • 易于单独测试每项职责
  • 对一项关注点的更改不会影响其他关注点
  • 类规模小且职责集中
开闭原则(OCP)

规则: 软件实体(类、模块、函数)应该对扩展开放,对修改关闭。

重要性: 应该能够在不更改现有代码的情况下添加新功能,从而降低破坏现有功能的风险。

实践中的 OCP

❌ 不佳 - 违反 OCP:

public class PaymentProcessor {
    public void processPayment(String paymentType, double amount) {
        if (paymentType.equals("CREDIT_CARD")) {
            // Process credit card payment
            System.out.println("Processing credit card payment: $" + amount);
        } else if (paymentType.equals("PAYPAL")) {
            // Process PayPal payment
            System.out.println("Processing PayPal payment: $" + amount);
        } else if (paymentType.equals("BITCOIN")) {
            // Process Bitcoin payment
            System.out.println("Processing Bitcoin payment: $" + amount);
        }
        // Adding new payment method requires modifying this class!
    }
}

问题:

  • 添加新的支付类型时必须修改 PaymentProcessor
  • 违反 OCP(未对修改关闭)
  • if-else 链不断增长
  • 难以单独测试各种支付类型

✅ 良好 - 遵循 OCP:

// Abstract payment interface
public interface PaymentMethod {
    void process(double amount);
}

// Concrete implementations
public class CreditCardPayment implements PaymentMethod {
    @Override
    public void process(double amount) {
        System.out.println("Processing credit card payment: $" + amount);
        // Credit card specific logic
    }
}

public class PayPalPayment implements PaymentMethod {
    @Override
    public void process(double amount) {
        System.out.println("Processing PayPal payment: $" + amount);
        // PayPal specific logic
    }
}

public class BitcoinPayment implements PaymentMethod {
    @Override
    public void process(double amount) {
        System.out.println("Processing Bitcoin payment: $" + amount);
        // Bitcoin specific logic
    }
}

// Processor delegates to payment method
public class PaymentProcessor {
    public void processPayment(PaymentMethod paymentMethod, double amount) {
        paymentMethod.process(amount);
    }
}

// Usage
PaymentProcessor processor = new PaymentProcessor();
processor.processPayment(new CreditCardPayment(), 100.0);
processor.processPayment(new PayPalPayment(), 50.0);

// Adding new payment method: just create new class, no modification needed!
public class ApplePayPayment implements PaymentMethod {
    @Override
    public void process(double amount) {
        System.out.println("Processing Apple Pay payment: $" + amount);
    }
}

优点:

  • 添加新的支付方式时无需修改现有代码
  • 每种支付类型都可以独立测试
  • 遵循 OCP:对扩展开放,对修改关闭
  • 关注点分离清晰
使用策略模式实现 OCP

✅ 进阶示例 - 折扣策略:

// Strategy interface
public interface DiscountStrategy {
    double applyDiscount(double price);
}

// Concrete strategies
public class NoDiscount implements DiscountStrategy {
    @Override
    public double applyDiscount(double price) {
        return price;
    }
}

public class PercentageDiscount implements DiscountStrategy {
    private final double percentage;

    public PercentageDiscount(double percentage) {
        this.percentage = percentage;
    }

    @Override
    public double applyDiscount(double price) {
        return price * (1 - percentage / 100);
    }
}

public class FixedAmountDiscount implements DiscountStrategy {
    private final double amount;

    public FixedAmountDiscount(double amount) {
        this.amount = amount;
    }

    @Override
    public double applyDiscount(double price) {
        return Math.max(0, price - amount);
    }
}

// Context uses strategy
public class PriceCalculator {
    private final DiscountStrategy discountStrategy;

    public PriceCalculator(DiscountStrategy discountStrategy) {
        this.discountStrategy = discountStrategy;
    }

    public double calculateFinalPrice(double originalPrice) {
        return discountStrategy.applyDiscount(originalPrice);
    }
}

// Usage
PriceCalculator calc1 = new PriceCalculator(new PercentageDiscount(10));
double price1 = calc1.calculateFinalPrice(100); // 90.0

PriceCalculator calc2 = new PriceCalculator(new FixedAmountDiscount(15));
double price2 = calc2.calculateFinalPrice(100); // 85.0
里氏替换原则(LSP)

规则: 在不破坏应用的前提下,超类对象应该可以替换为子类对象。子类型必须能够替代其基类型。

重要性: 违反 LSP 会导致意外行为并破坏多态性。

实践中的 LSP

❌ 错误 - 违反 LSP:

public class Rectangle {
    protected int width;
    protected int height;

    public void setWidth(int width) {
        this.width = width;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int getArea() {
        return width * height;
    }
}

// Square violates LSP because it changes behavior of setters
public class Square extends Rectangle {
    @Override
    public void setWidth(int width) {
        this.width = width;
        this.height = width; // Side effect!
    }

    @Override
    public void setHeight(int height) {
        this.width = height; // Side effect!
        this.height = height;
    }
}

// This test works for Rectangle but fails for Square
public void testRectangle(Rectangle rect) {
    rect.setWidth(5);
    rect.setHeight(4);
    assertEquals(20, rect.getArea()); // Fails for Square! (25 instead of 20)
}

问题:

  • Square 改变了 Rectangle 方法的行为
  • 无法用 Square 替代 Rectangle
  • 违反 LSP,破坏了多态性

✅ 正确 - 遵循 LSP:

// Common interface for shapes
public interface Shape {
    int getArea();
}

// Rectangle implementation
public class Rectangle implements Shape {
    private final int width;
    private final int height;

    public Rectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }

    @Override
    public int getArea() {
        return width * height;
    }

    public int getWidth() { return width; }
    public int getHeight() { return height; }
}

// Square implementation (no inheritance from Rectangle)
public class Square implements Shape {
    private final int side;

    public Square(int side) {
        this.side = side;
    }

    @Override
    public int getArea() {
        return side * side;
    }

    public int getSide() { return side; }
}

// Works for any Shape
public int calculateTotalArea(List<Shape> shapes) {
    return shapes.stream()
                 .mapToInt(Shape::getArea)
                 .sum();
}

优点:

  • Square 和 Rectangle 相互独立
  • 两者都正确实现了 Shape 契约
  • 可以替换为任意 Shape 实现
  • 不会出现意外行为
LSP - 前置条件和后置条件

✅ 正确 - 保持契约:

public interface BankAccount {
    // Precondition: amount > 0
    // Postcondition: balance increased by amount
    void deposit(double amount);

    // Precondition: amount > 0 and amount <= balance
    // Postcondition: balance decreased by amount
    void withdraw(double amount) throws InsufficientFundsException;

    double getBalance();
}

public class SavingsAccount implements BankAccount {
    private double balance;

    @Override
    public void deposit(double amount) {
        if (amount <= 0) {
            throw new IllegalArgumentException("Amount must be positive");
        }
        balance += amount;
    }

    @Override
    public void withdraw(double amount) throws InsufficientFundsException {
        if (amount <= 0) {
            throw new IllegalArgumentException("Amount must be positive");
        }
        if (amount > balance) {
            throw new InsufficientFundsException();
        }
        balance -= amount;
    }

    @Override
    public double getBalance() {
        return balance;
    }
}

// Subclass maintains contracts (LSP)
public class CheckingAccount implements BankAccount {
    private double balance;
    private final double overdraftLimit;

    public CheckingAccount(double overdraftLimit) {
        this.overdraftLimit = overdraftLimit;
    }

    @Override
    public void deposit(double amount) {
        if (amount <= 0) {
            throw new IllegalArgumentException("Amount must be positive");
        }
        balance += amount; // Same postcondition
    }

    @Override
    public void withdraw(double amount) throws InsufficientFundsException {
        if (amount <= 0) {
            throw new IllegalArgumentException("Amount must be positive");
        }
        // Can weaken precondition (allow overdraft) but not strengthen
        if (amount > balance + overdraftLimit) {
            throw new InsufficientFundsException();
        }
        balance -= amount; // Same postcondition
    }

    @Override
    public double getBalance() {
        return balance;
    }
}
接口隔离原则(ISP)

规则: 不应强迫客户端依赖它们不使用的接口。多个特定接口优于一个通用接口。

重要性: 大型接口会迫使实现提供不需要的方法,导致空实现和紧密耦合。

实践中的 ISP

❌ 错误 - 臃肿的接口:

// Fat interface forces all implementations to provide all methods
public interface Worker {
    void work();
    void eat();
    void sleep();
    void getSalary();
    void attendMeeting();
}

// Robot doesn't eat or sleep but is forced to implement these methods
public class RobotWorker implements Worker {
    @Override
    public void work() {
        System.out.println("Robot working");
    }

    @Override
    public void eat() {
        // Doesn't make sense for robots!
        throw new UnsupportedOperationException("Robots don't eat");
    }

    @Override
    public void sleep() {
        // Doesn't make sense for robots!
        throw new UnsupportedOperationException("Robots don't sleep");
    }

    @Override
    public void getSalary() {
        throw new UnsupportedOperationException("Robots don't get paid");
    }

    @Override
    public void attendMeeting() {
        System.out.println("Robot attending meeting");
    }
}

问题:

  • Robot 被迫实现生物相关的方法
  • 抛出 UnsupportedOperationException 是一种代码异味
  • 违反 ISP
  • 与无关方法紧密耦合

✅ 正确 - 拆分接口:

// Segregated interfaces - clients depend only on what they need
public interface Workable {
    void work();
}

public interface Eatable {
    void eat();
}

public interface Sleepable {
    void sleep();
}

public interface Payable {
    void getSalary();
}

public interface MeetingAttendee {
    void attendMeeting();
}

// Human implements relevant interfaces
public class HumanWorker implements Workable, Eatable, Sleepable, Payable, MeetingAttendee {
    @Override
    public void work() {
        System.out.println("Human working");
    }

    @Override
    public void eat() {
        System.out.println("Human eating");
    }

    @Override
    public void sleep() {
        System.out.println("Human sleeping");
    }

    @Override
    public void getSalary() {
        System.out.println("Human receiving salary");
    }

    @Override
    public void attendMeeting() {
        System.out.println("Human attending meeting");
    }
}

// Robot only implements relevant interfaces
public class RobotWorker implements Workable, MeetingAttendee {
    @Override
    public void work() {
        System.out.println("Robot working");
    }

    @Override
    public void attendMeeting() {
        System.out.println("Robot attending meeting");
    }
}

优点:

  • 实现只需提供合理的方法
  • 无需使用 UnsupportedOperationException
  • 关注点分离清晰
  • 组合灵活
依赖倒置原则(DIP)

规则: 高层模块不应依赖低层模块。两者都应依赖抽象。抽象不应依赖细节,细节应依赖抽象。

重要性: DIP 能解耦代码,使其更加灵活、易于测试和维护。

实践中的 DIP

❌ 错误 - 高层依赖低层:

// Low-level module
public class MySQLDatabase {
    public void save(String data) {
        System.out.println("Saving to MySQL: " + data);
    }
}

// High-level module depends on concrete low-level module
public class UserService {
    private MySQLDatabase database; // Concrete dependency!

    public UserService() {
        this.database = new MySQLDatabase(); // Tight coupling!
    }

    public void createUser(String userData) {
        // Business logic
        database.save(userData);
    }
}

问题:

  • UserService 与 MySQLDatabase 紧密耦合
  • 修改 UserService 才能切换到 PostgreSQL
  • 难以测试(无法模拟数据库)
  • 违反 DIP

✅ 正确 - 两者都依赖抽象:

// Abstraction
public interface Database {
    void save(String data);
}

// Low-level modules depend on abstraction
public class MySQLDatabase implements Database {
    @Override
    public void save(String data) {
        System.out.println("Saving to MySQL: " + data);
    }
}

public class PostgreSQLDatabase implements Database {
    @Override
    public void save(String data) {
        System.out.println("Saving to PostgreSQL: " + data);
    }
}

public class MongoDatabase implements Database {
    @Override
    public void save(String data) {
        System.out.println("Saving to MongoDB: " + data);
    }
}

// High-level module depends on abstraction
public class UserService {
    private final Database database; // Abstraction!

    // Dependency injected through constructor
    public UserService(Database database) {
        this.database = database;
    }

    public void createUser(String userData) {
        // Business logic
        database.save(userData);
    }
}

// Usage - client chooses implementation
Database db = new MySQLDatabase();
UserService service = new UserService(db);
service.createUser("John Doe");

// Easy to switch implementations
Database postgresDb = new PostgreSQLDatabase();
UserService postgresService = new UserService(postgresDb);

// Easy to test with mock
Database mockDb = mock(Database.class);
UserService testService = new UserService(mockDb);

优点:

  • UserService 与数据库实现解耦
  • 易于切换数据库实现
  • 易于使用模拟对象进行测试
  • 遵循 DIP
Spring Framework 中的 SOLID 原则

Spring Framework 基于 SOLID 原则构建,尤其是依赖倒置原则。

Spring 中的依赖注入

✅ Spring DI 示例:

// Abstraction
public interface UserRepository {
    User findById(Long id);
    void save(User user);
}

// Implementation
@Repository
public class JpaUserRepository implements UserRepository {
    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public User findById(Long id) {
        return entityManager.find(User.class, id);
    }

    @Override
    public void save(User user) {
        entityManager.persist(user);
    }
}

// Service depends on abstraction
@Service
public class UserService {
    private final UserRepository userRepository;

    // Constructor injection (recommended)
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User getUser(Long id) {
        return userRepository.findById(id);
    }
}

// Controller depends on service abstraction
@RestController
@RequestMapping("/users")
public class UserController {
    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/{id}")
    public ResponseEntity<User> getUser(@PathVariable Long id) {
        User user = userService.getUser(id);
        return ResponseEntity.ok(user);
    }
}

Spring DI 最佳实践:

  • 使用构造函数注入(适用于必需依赖,保证不可变性)
  • 仅对可选依赖优先考虑字段注入
  • 依赖接口,而非具体类
  • 存在多个实现时使用 @Qualifier

DRY(不要重复自己)

规则: 系统中的每一项知识都必须有唯一、明确且权威的表示。

重要性: 重复会导致不一致、维护难度增加和更多错误。

识别代码重复

❌ 错误 - 明显重复:

public class OrderService {
    public void processOnlineOrder(Order order) {
        // Validate
        if (order == null) {
            throw new IllegalArgumentException("Order cannot be null");
        }
        if (order.getItems().isEmpty()) {
            throw new IllegalArgumentException("Order must have items");
        }
        if (order.getTotalAmount() <= 0) {
            throw new IllegalArgumentException("Order total must be positive");
        }

        // Process
        System.out.println("Processing online order: " + order.getId());
        order.setStatus(OrderStatus.PROCESSING);
        saveOrder(order);
    }

    public void processPhoneOrder(Order order) {
        // Same validation - DUPLICATION!
        if (order == null) {
            throw new IllegalArgumentException("Order cannot be null");
        }
        if (order.getItems().isEmpty()) {
            throw new IllegalArgumentException("Order must have items");
        }
        if (order.getTotalAmount() <= 0) {
            throw new IllegalArgumentException("Order total must be positive");
        }

        // Process
        System.out.println("Processing phone order: " + order.getId());
        order.setStatus(OrderStatus.PROCESSING);
        saveOrder(order);
    }
}

✅ 正确 - 提取通用逻辑:

public class OrderService {
    public void processOnlineOrder(Order order) {
        validateOrder(order);
        processOrder(order, "online");
    }

    public void processPhoneOrder(Order order) {
        validateOrder(order);
        processOrder(order, "phone");
    }

    private void validateOrder(Order order) {
        if (order == null) {
            throw new IllegalArgumentException("Order cannot be null");
        }
        if (order.getItems().isEmpty()) {
            throw new IllegalArgumentException("Order must have items");
        }
        if (order.getTotalAmount() <= 0) {
            throw new IllegalArgumentException("Order total must be positive");
        }
    }

    private void processOrder(Order order, String type) {
        System.out.println("Processing " + type + " order: " + order.getId());
        order.setStatus(OrderStatus.PROCESSING);
        saveOrder(order);
    }
}
工具类和辅助方法

✅ 为可复用逻辑创建工具类:

public final class StringUtils {
    private StringUtils() {
        // Prevent instantiation
    }

    public static boolean isBlank(String str) {
        return str == null || str.trim().isEmpty();
    }

    public static String capitalize(String str) {
        if (isBlank(str)) {
            return str;
        }
        return str.substring(0, 1).toUpperCase() + str.substring(1).toLowerCase();
    }

    public static String truncate(String str, int maxLength) {
        if (str == null || str.length() <= maxLength) {
            return str;
        }
        return str.substring(0, maxLength) + "...";
    }
}

// Usage
if (StringUtils.isBlank(username)) {
    throw new ValidationException("Username is required");
}

String displayName = StringUtils.capitalize(name);
使用泛型提高复用性

✅ 使用泛型避免重复:

// Instead of creating separate classes for different types
public class GenericRepository<T, ID> {
    private final Class<T> entityClass;

    @PersistenceContext
    private EntityManager entityManager;

    public GenericRepository(Class<T> entityClass) {
        this.entityClass = entityClass;
    }

    public Optional<T> findById(ID id) {
        T entity = entityManager.find(entityClass, id);
        return Optional.ofNullable(entity);
    }

    public List<T> findAll() {
        CriteriaQuery<T> query = entityManager.getCriteriaBuilder()
            .createQuery(entityClass);
        query.select(query.from(entityClass));
        return entityManager.createQuery(query).getResultList();
    }

    public void save(T entity) {
        entityManager.persist(entity);
    }

    public void delete(T entity) {
        entityManager.remove(entity);
    }
}

// Concrete repositories extend generic repository
@Repository
public class UserRepository extends GenericRepository<User, Long> {
    public UserRepository() {
        super(User.class);
    }

    // Add User-specific queries
    public Optional<User> findByEmail(String email) {
        // Custom query
    }
}

整洁代码原则

有意义的名称

规则: 名称应体现意图、易于读出且便于搜索。

❌ 不好的名称:

int d; // elapsed time in days
String yyyymmdd;
List<int[]> list1;

public void getData() {
    // What data?
}

✅ 好的名称:

int elapsedTimeInDays;
String formattedDate;
List<Customer> activeCustomers;

public Customer getCustomerById(Long customerId) {
    // Clear what this method does
}
命名约定
// Classes: PascalCase, nouns
public class CustomerService { }
public class OrderRepository { }

// Interfaces: PascalCase, often adjectives or nouns
public interface Serializable { }
public interface UserRepository { }

// Methods: camelCase, verbs
public void calculateTotal() { }
public Customer findCustomerById(Long id) { }

// Variables: camelCase, nouns
String customerName;
int orderCount;
boolean isActive;

// Constants: UPPER_SNAKE_CASE
public static final int MAX_RETRY_COUNT = 3;
public static final String DEFAULT_ENCODING = "UTF-8";

// Packages: lowercase, periods
package com.example.service;
package com.example.repository;

// Boolean methods/variables: is, has, can
boolean isValid();
boolean hasPermission();
boolean canExecute();
函数大小和复杂度

规则: 函数应短小且只做一件事。每个方法以 5-20 行为宜。

❌ 错误 - 过于庞大、复杂的方法:

public void processOrder(Order order) {
    // Validation
    if (order == null) throw new IllegalArgumentException();
    if (order.getItems().isEmpty()) throw new IllegalArgumentException();

    // Calculate total
    double total = 0;
    for (OrderItem item : order.getItems()) {
        double itemPrice = item.getPrice();
        int quantity = item.getQuantity();
        double discount = item.getDiscount();
        total += (itemPrice * quantity) * (1 - discount);
    }
    order.setTotal(total);

    // Apply coupon
    if (order.getCoupon() != null) {
        String couponCode = order.getCoupon().getCode();
        if (couponCode.startsWith("SAVE")) {
            total *= 0.9;
        } else if (couponCode.startsWith("BIG")) {
            total *= 0.8;
        }
        order.setTotal(total);
    }

    // Check inventory
    for (OrderItem item : order.getItems()) {
        int available = inventoryService.getAvailableQuantity(item.getProductId());
        if (available < item.getQuantity()) {
            throw new InsufficientInventoryException();
        }
    }

    // Save order
    orderRepository.save(order);

    // Send email
    emailService.send(order.getCustomer().getEmail(), "Order Confirmation",
        "Your order " + order.getId() + " has been confirmed");

    // Update inventory
    for (OrderItem item : order.getItems()) {
        inventoryService.decrementQuantity(item.getProductId(), item.getQuantity());
    }
}

✅ 正确 - 短小、专注的方法:

public void processOrder(Order order) {
    validateOrder(order);
    calculateOrderTotal(order);
    applyCouponDiscount(order);
    checkInventoryAvailability(order);
    saveOrder(order);
    sendConfirmationEmail(order);
    updateInventory(order);
}

private void validateOrder(Order order) {
    if (order == null) {
        throw new IllegalArgumentException("Order cannot be null");
    }
    if (order.getItems().isEmpty()) {
        throw new IllegalArgumentException("Order must contain items");
    }
}

private void calculateOrderTotal(Order order) {
    double total = order.getItems().stream()
        .mapToDouble(this::calculateItemTotal)
        .sum();
    order.setTotal(total);
}

private double calculateItemTotal(OrderItem item) {
    return item.getPrice() * item.getQuantity() * (1 - item.getDiscount());
}

private void applyCouponDiscount(Order order) {
    if (order.getCoupon() == null) {
        return;
    }

    double discountMultiplier = getDiscountMultiplier(order.getCoupon());
    order.setTotal(order.getTotal() * discountMultiplier);
}

private double getDiscountMultiplier(Coupon coupon) {
    String code = coupon.getCode();
    if (code.startsWith("SAVE")) return 0.9;
    if (code.startsWith("BIG")) return 0.8;
    return 1.0;
}

private void checkInventoryAvailability(Order order) {
    for (OrderItem item : order.getItems()) {
        int available = inventoryService.getAvailableQuantity(item.getProductId());
        if (available < item.getQuantity()) {
            throw new InsufficientInventoryException(
                "Product " + item.getProductId() + " has insufficient inventory");
        }
    }
}

private void saveOrder(Order order) {
    orderRepository.save(order);
}

private void sendConfirmationEmail(Order order) {
    String email = order.getCustomer().getEmail();
    String subject = "Order Confirmation";
    String body = String.format("Your order %s has been confirmed", order.getId());
    emailService.send(email, subject, body);
}

private void updateInventory(Order order) {
    order.getItems().forEach(item ->
        inventoryService.decrementQuantity(item.getProductId(), item.getQuantity())
    );
}

优点:

  • 每个方法都有明确且单一的用途
  • 易于理解和测试
  • 主方法读起来像目录
  • 辅助方法可复用
注释最佳实践

规则: 代码应该能自我说明。注释应解释原因,而不是内容。

❌ 不好的注释:

// Set the flag to true
isActive = true;

// Loop through users
for (User user : users) {
    // Check if user is active
    if (user.isActive()) {
        // Add to list
        activeUsers.add(user);
    }
}

// This is the UserService class
public class UserService {
}

✅ 好的注释:

// No comment needed - code is self-explanatory
isActive = true;

List<User> activeUsers = users.stream()
    .filter(User::isActive)
    .collect(Collectors.toList());

// Good: Explains WHY, not WHAT
// We use exponential backoff to avoid overwhelming the external API
// after multiple failures (circuit breaker pattern)
private int calculateRetryDelay(int attemptNumber) {
    return (int) Math.pow(2, attemptNumber) * 1000;
}

// Good: JavaDoc for public API
/**
 * Transfers funds between accounts atomically.
 *
 * @param fromAccount source account (must have sufficient balance)
 * @param toAccount destination account
 * @param amount amount to transfer (must be positive)
 * @throws InsufficientFundsException if source account lacks funds
 * @throws IllegalArgumentException if amount is negative or zero
 */
public void transferFunds(Account fromAccount, Account toAccount, double amount)
    throws InsufficientFundsException {
    // Implementation
}

// Good: Explains non-obvious business rule
// Tax calculation excludes shipping but includes discount adjustments
// per IRS regulation 2024-15
double taxableAmount = subtotal - discount;

何时添加注释:

  • 公共 API(JavaDoc)
  • 复杂算法(解释思路)
  • 业务规则(监管要求)
  • 变通方案(说明为何需要该方案)
  • TODO/FIXME(附上工单编号)

何时不添加注释:

  • 显而易见的代码
  • 已注释掉的代码(删除它,使用版本控制)
  • 变更日志(使用 git)
错误处理

规则: 对异常情况使用异常。不要用异常控制流程。

❌ 不好的错误处理:

// Using exceptions for control flow
public User findUser(Long id) {
    try {
        return userRepository.findById(id);
    } catch (NotFoundException e) {
        return null; // Swallowing exception
    }
}

// Catching generic Exception
public void processData(String data) {
    try {
        // Complex logic
    } catch (Exception e) {
        // Too broad!
    }
}

// Empty catch block
try {
    riskyOperation();
} catch (IOException e) {
    // Ignored - NEVER DO THIS
}

✅ 好的错误处理:

// Use Optional for "not found" scenarios
public Optional<User> findUser(Long id) {
    return userRepository.findById(id);
}

// Catch specific exceptions
public void processData(String data) {
    try {
        parseAndValidate(data);
        saveToDatabase(data);
    } catch (JsonParseException e) {
        log.error("Failed to parse JSON data: {}", data, e);
        throw new DataProcessingException("Invalid JSON format", e);
    } catch (DataAccessException e) {
        log.error("Database error while saving data", e);
        throw new DataProcessingException("Failed to save data", e);
    }
}

// Always handle or rethrow exceptions
try {
    riskyOperation();
} catch (IOException e) {
    log.error("Operation failed", e);
    throw new ApplicationException("Failed to perform operation", e);
}

// Use try-with-resources for auto-closeable resources
public String readFile(String path) throws IOException {
    try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
        return reader.lines().collect(Collectors.joining("\n"));
    }
    // Reader automatically closed, even if exception occurs
}
代码组织

规则: 在类中按逻辑组织代码。相关方法应放在一起。

✅ 良好的类组织:

public class UserService {
    // 1. Constants
    private static final int MAX_LOGIN_ATTEMPTS = 3;
    private static final long LOCKOUT_DURATION_MINUTES = 30;

    // 2. Static fields
    private static final Logger log = LoggerFactory.getLogger(UserService.class);

    // 3. Instance fields
    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;
    private final EmailService emailService;

    // 4. Constructors
    public UserService(UserRepository userRepository,
                       PasswordEncoder passwordEncoder,
                       EmailService emailService) {
        this.userRepository = userRepository;
        this.passwordEncoder = passwordEncoder;
        this.emailService = emailService;
    }

    // 5. Public methods (grouped by functionality)

    // User creation methods
    public User registerUser(UserRegistrationDto dto) {
        validateRegistration(dto);
        User user = createUser(dto);
        sendWelcomeEmail(user);
        return user;
    }

    // User authentication methods
    public AuthToken login(String email, String password) {
        User user = findUserByEmail(email);
        validatePassword(user, password);
        return generateAuthToken(user);
    }

    // 6. Private helper methods (near methods that use them)

    private void validateRegistration(UserRegistrationDto dto) {
        // Validation logic
    }

    private User createUser(UserRegistrationDto dto) {
        // Creation logic
    }

    private void sendWelcomeEmail(User user) {
        emailService.send(user.getEmail(), "Welcome!", getWelcomeEmailBody());
    }

    private User findUserByEmail(String email) {
        return userRepository.findByEmail(email)
            .orElseThrow(() -> new UserNotFoundException(email));
    }

    private void validatePassword(User user, String password) {
        if (!passwordEncoder.matches(password, user.getPasswordHash())) {
            throw new AuthenticationException("Invalid password");
        }
    }

    private AuthToken generateAuthToken(User user) {
        // Token generation logic
    }

    private String getWelcomeEmailBody() {
        return "Welcome to our application!";
    }
}

Java 特定最佳实践

使用 Optional 代替 Null

规则: 使用 Optional<T> 表示可能不存在的值。集合永远不要返回 null。

❌ 不好的做法——返回 Null:

public User findUser(Long id) {
    User user = database.find(id);
    return user; // May return null!
}

// Caller must remember to check null
User user = findUser(123L);
if (user != null) {
    // Use user
}

✅ 好的做法——使用 Optional:

public Optional<User> findUser(Long id) {
    User user = database.find(id);
    return Optional.ofNullable(user);
}

// Caller forced to handle absence
Optional<User> userOpt = findUser(123L);

// Method 1: ifPresent
userOpt.ifPresent(user -> System.out.println(user.getName()));

// Method 2: orElse
User user = userOpt.orElse(createDefaultUser());

// Method 3: orElseThrow
User user = userOpt.orElseThrow(() ->
    new UserNotFoundException("User 123 not found"));

// Method 4: map/flatMap
String email = userOpt
    .map(User::getEmail)
    .orElse("[email protected]");

Optional 最佳实践:

  • 对可能找不到值的方法,返回 Optional<T>
  • 字段永远不要使用 Optional
  • 永远不要将 Optional 作为方法参数传递
  • 返回 Optional 的方法永远不要返回 null
  • 使用 Optional.empty(),而不是 Optional.ofNullable(null)

❌ 不好的 Optional 用法:

// Don't use Optional as field
public class User {
    private Optional<String> middleName; // BAD!
}

// Don't use Optional as parameter
public void setEmail(Optional<String> email) { // BAD!
}

// Don't call get() without checking
Optional<User> userOpt = findUser(id);
User user = userOpt.get(); // May throw NoSuchElementException!

✅ 好的 Optional 用法:

// Use null for optional fields (or use proper null handling)
public class User {
    private String middleName; // Can be null

    public Optional<String> getMiddleName() {
        return Optional.ofNullable(middleName);
    }
}

// Use regular parameter with @Nullable annotation
public void setEmail(@Nullable String email) {
    this.email = email;
}

// Always check before get(), or use other methods
Optional<User> userOpt = findUser(id);
if (userOpt.isPresent()) {
    User user = userOpt.get();
    // Use user
}

// Or use orElse/orElseThrow/ifPresent
User user = userOpt.orElseThrow(() -> new NotFoundException());
优先使用组合,而非继承

规则: 优先使用组合(has-a),而不是继承(is-a),除非确实存在 is-a 关系。

❌ 不好的做法——滥用继承:

// Inheritance used just to reuse code (wrong!)
public class Stack extends ArrayList<Object> {
    public void push(Object item) {
        add(item);
    }

    public Object pop() {
        return remove(size() - 1);
    }
}

// Problems:
// 1. Stack exposes all ArrayList methods (add, remove, clear, etc.)
// 2. Stack IS-NOT-A ArrayList semantically
// 3. Breaks encapsulation

✅ 好的做法——组合:

public class Stack<T> {
    private final List<T> elements = new ArrayList<>();

    public void push(T item) {
        elements.add(item);
    }

    public T pop() {
        if (elements.isEmpty()) {
            throw new EmptyStackException();
        }
        return elements.remove(elements.size() - 1);
    }

    public T peek() {
        if (elements.isEmpty()) {
            throw new EmptyStackException();
        }
        return elements.get(elements.size() - 1);
    }

    public boolean isEmpty() {
        return elements.isEmpty();
    }

    public int size() {
        return elements.size();
    }
}

何时使用继承:

  • 存在真正的 is-a 关系
  • 子类是超类的专门化版本
  • 满足 Liskov 替换原则

何时使用组合:

  • 复用功能
  • 存在 has-a 关系
  • 需要灵活更改实现
  • 需要多种行为(可以组合多个,但只能继承一个)
使用 Final 实现不可变性

规则: 尽可能让类和变量不可变。广泛使用 final。

✅ 不可变类:

public final class Money {
    private final double amount;
    private final String currency;

    public Money(double amount, String currency) {
        if (amount < 0) {
            throw new IllegalArgumentException("Amount cannot be negative");
        }
        this.amount = amount;
        this.currency = currency;
    }

    public double getAmount() {
        return amount;
    }

    public String getCurrency() {
        return currency;
    }

    // Return new instance instead of modifying
    public Money add(Money other) {
        if (!this.currency.equals(other.currency)) {
            throw new IllegalArgumentException("Currency mismatch");
        }
        return new Money(this.amount + other.amount, this.currency);
    }

    public Money multiply(double multiplier) {
        return new Money(this.amount * multiplier, this.currency);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Money)) return false;
        Money money = (Money) o;
        return Double.compare(money.amount, amount) == 0
            && currency.equals(money.currency);
    }

    @Override
    public int hashCode() {
        return Objects.hash(amount, currency);
    }
}

不可变性的优点:

  • 默认线程安全
  • 可以安全共享
  • 更容易推理
  • 无需防御性复制
  • 可安全用作 HashMap 键

✅ 使用 Final 修饰变量:

public void processOrder(Order order) {
    final double total = order.getTotal();
    final List<OrderItem> items = order.getItems();

    // Compiler prevents reassignment
    // total = 100; // Compilation error
    // items = new ArrayList<>(); // Compilation error

    // Note: final prevents reassignment, not mutation
    items.add(new OrderItem()); // This is allowed!

    // For true immutability, use Collections.unmodifiableList
    final List<OrderItem> immutableItems =
        Collections.unmodifiableList(new ArrayList<>(items));
}
Stream API 的使用

规则: 使用 Stream API 操作集合。它更易读、符合函数式风格,也可以并行化。

❌ 不好的做法——命令式风格:

List<User> activeUsers = new ArrayList<>();
for (User user : users) {
    if (user.isActive()) {
        activeUsers.add(user);
    }
}

List<String> emails = new ArrayList<>();
for (User user : activeUsers) {
    emails.add(user.getEmail());
}

Collections.sort(emails);

✅ 好的做法——使用 Stream 的声明式风格:

List<String> emails = users.stream()
    .filter(User::isActive)
    .map(User::getEmail)
    .sorted()
    .collect(Collectors.toList());

✅ 常见 Stream 模式:

// Filtering
List<Order> largeOrders = orders.stream()
    .filter(order -> order.getTotal() > 1000)
    .collect(Collectors.toList());

// Mapping
List<String> customerNames = orders.stream()
    .map(order -> order.getCustomer().getName())
    .collect(Collectors.toList());

// FlatMap (flatten nested collections)
List<OrderItem> allItems = orders.stream()
    .flatMap(order -> order.getItems().stream())
    .collect(Collectors.toList());

// Reduce (sum, average, etc.)
double totalRevenue = orders.stream()
    .mapToDouble(Order::getTotal)
    .sum();

Optional<Order> maxOrder = orders.stream()
    .max(Comparator.comparing(Order::getTotal));

// Grouping
Map<String, List<Order>> ordersByStatus = orders.stream()
    .collect(Collectors.groupingBy(Order::getStatus));

// Partitioning (special case of grouping - boolean)
Map<Boolean, List<Order>> ordersByShipped = orders.stream()
    .collect(Collectors.partitioningBy(Order::isShipped));

// Find first/any
Optional<User> firstAdmin = users.stream()
    .filter(User::isAdmin)
    .findFirst();

// Distinct
List<String> uniqueCities = users.stream()
    .map(User::getCity)
    .distinct()
    .collect(Collectors.toList());

// Limit and skip
List<User> firstTenUsers = users.stream()
    .limit(10)
    .collect(Collectors.toList());

// Combining operations
double averageOrderValueForActiveCustomers = orders.stream()
    .filter(order -> order.getCustomer().isActive())
    .mapToDouble(Order::getTotal)
    .average()
    .orElse(0.0);

Stream 最佳实践:

  • 不要重复使用流(为每条流水线创建新流)
  • 避免在流操作中产生副作用
  • 尽可能使用方法引用
  • 对大型数据集考虑使用并行流(但要进行测量!)
  • Stream 是惰性的——终端操作会触发执行

❌ 不好的 Stream 用法:

// Don't modify external state in streams
List<String> results = new ArrayList<>();
users.stream()
    .forEach(user -> results.add(user.getName())); // Side effect!

// Use collect instead
List<String> results = users.stream()
    .map(User::getName)
    .collect(Collectors.toList());

// Don't reuse streams
Stream<User> userStream = users.stream();
long count = userStream.count(); // OK
List<User> list = userStream.collect(Collectors.toList()); // IllegalStateException!
Lambda 表达式

规则: 对函数式接口使用 Lambda 表达式。适用时优先使用方法引用。

✅ Lambda 最佳实践:

// Lambda expression
List<User> sorted = users.stream()
    .sorted((u1, u2) -> u1.getName().compareTo(u2.getName()))
    .collect(Collectors.toList());

// Better: Method reference
List<User> sorted = users.stream()
    .sorted(Comparator.comparing(User::getName))
    .collect(Collectors.toList());

// Lambda with multiple statements
users.forEach(user -> {
    user.setLastLoginTime(LocalDateTime.now());
    user.incrementLoginCount();
    userRepository.save(user);
});

// Custom functional interface
@FunctionalInterface
public interface OrderProcessor {
    void process(Order order);
}

OrderProcessor processor = order -> {
    validateOrder(order);
    calculateTotal(order);
    saveOrder(order);
};
方法引用

规则: 尽可能使用方法引用代替 Lambda 表达式。它们更简洁。

// Lambda vs Method Reference

// Lambda: user -> user.getName()
// Method reference: User::getName
users.stream().map(User::getName);

// Lambda: user -> System.out.println(user)
// Method reference: System.out::println
users.forEach(System.out::println);

// Lambda: () -> new ArrayList<>()
// Method reference: ArrayList::new
Supplier<List<String>> supplier = ArrayList::new;

// Lambda: str -> str.length()
// Method reference: String::length
strings.stream().map(String::length);

方法引用的类型:

  1. 静态方法:ClassName::staticMethod
  2. 特定对象的实例方法:object::instanceMethod
  3. 任意对象的实例方法:ClassName::instanceMethod
  4. 构造函数:ClassName::new
Try-With-Resources

规则: 对 AutoCloseable 资源始终使用 try-with-resources。

❌ 不好的做法——手动管理资源:

BufferedReader reader = null;
try {
    reader = new BufferedReader(new FileReader("file.txt"));
    String line = reader.readLine();
    // Process line
} catch (IOException e) {
    log.error("Error reading file", e);
} finally {
    if (reader != null) {
        try {
            reader.close();
        } catch (IOException e) {
            log.error("Error closing reader", e);
        }
    }
}

✅ 好的做法——Try-With-Resources:

try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
    String line = reader.readLine();
    // Process line
} catch (IOException e) {
    log.error("Error reading file", e);
}
// Reader automatically closed, even if exception occurs

// Multiple resources
try (FileInputStream fis = new FileInputStream("input.txt");
     FileOutputStream fos = new FileOutputStream("output.txt")) {
    // Use streams
} catch (IOException e) {
    log.error("Error processing files", e);
}
// Both streams automatically closed in reverse order
StringBuilder 与字符串拼接

规则: 在循环中多次拼接字符串时使用 StringBuilder。简单拼接时使用 +。

❌ 不好的做法——在循环中拼接字符串:

String result = "";
for (int i = 0; i < 1000; i++) {
    result += i + ","; // Creates 1000 new String objects!
}

✅ 好的做法——使用 StringBuilder:

StringBuilder builder = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    builder.append(i).append(",");
}
String result = builder.toString();

// Or use String.join for collections
List<String> items = Arrays.asList("apple", "banana", "cherry");
String result = String.join(", ", items);

// Or use Streams
String result = IntStream.range(0, 1000)
    .mapToObj(String::valueOf)
    .collect(Collectors.joining(","));

✅ 简单拼接——使用 + 即可:

// OK for simple cases (compiler optimizes)
String fullName = firstName + " " + lastName;
String message = "Hello, " + name + "!";

// Not OK in loops
for (String item : items) {
    result = result + item; // BAD!
}
枚举用法

**规则:**使用枚举表示固定的常量集合。枚举可以包含字段、方法和构造函数。

✅ 基本枚举:

public enum OrderStatus {
    PENDING,
    PROCESSING,
    SHIPPED,
    DELIVERED,
    CANCELLED
}

// Usage
Order order = new Order();
order.setStatus(OrderStatus.PENDING);

if (order.getStatus() == OrderStatus.DELIVERED) {
    // Process delivery
}

✅ 包含字段和方法的枚举:

public enum PaymentMethod {
    CREDIT_CARD("Credit Card", 2.9),
    DEBIT_CARD("Debit Card", 1.5),
    PAYPAL("PayPal", 3.5),
    BITCOIN("Bitcoin", 1.0);

    private final String displayName;
    private final double transactionFeePercent;

    PaymentMethod(String displayName, double transactionFeePercent) {
        this.displayName = displayName;
        this.transactionFeePercent = transactionFeePercent;
    }

    public String getDisplayName() {
        return displayName;
    }

    public double calculateFee(double amount) {
        return amount * (transactionFeePercent / 100);
    }

    public double calculateTotal(double amount) {
        return amount + calculateFee(amount);
    }
}

// Usage
PaymentMethod method = PaymentMethod.CREDIT_CARD;
double total = method.calculateTotal(100.0); // 102.90
System.out.println("Paying with: " + method.getDisplayName());

✅ 包含抽象方法的枚举(策略模式):

public enum Operation {
    PLUS {
        @Override
        public double apply(double x, double y) {
            return x + y;
        }
    },
    MINUS {
        @Override
        public double apply(double x, double y) {
            return x - y;
        }
    },
    MULTIPLY {
        @Override
        public double apply(double x, double y) {
            return x * y;
        }
    },
    DIVIDE {
        @Override
        public double apply(double x, double y) {
            if (y == 0) throw new ArithmeticException("Division by zero");
            return x / y;
        }
    };

    public abstract double apply(double x, double y);
}

// Usage
double result = Operation.PLUS.apply(5, 3); // 8.0

异常处理

受检异常与非受检异常

**规则:**可恢复的情况使用受检异常,编程错误使用非受检异常。

受检异常:

  • 继承 Exception(而非 RuntimeException)
  • 必须在方法签名中声明或捕获
  • 用于调用方能够合理处理的情况

非受检异常:

  • 继承 RuntimeException
  • 无需声明或捕获
  • 用于编程错误

✅ 何时使用:

// Checked exception - caller can handle
public User findUserById(Long id) throws UserNotFoundException {
    return userRepository.findById(id)
        .orElseThrow(() -> new UserNotFoundException("User not found: " + id));
}

// Unchecked exception - programming error
public void setAge(int age) {
    if (age < 0) {
        throw new IllegalArgumentException("Age cannot be negative");
    }
    this.age = age;
}

// Checked exception - I/O operation
public String readFile(String path) throws IOException {
    return Files.readString(Paths.get(path));
}

// Unchecked exception - null argument (programming error)
public void processOrder(Order order) {
    Objects.requireNonNull(order, "Order cannot be null");
    // Process order
}
何时捕获,何时抛出

**规则:**仅在能够有意义地处理异常时才捕获。否则,让异常继续传播。

❌ 不佳——捕获后重新抛出:

public void processData(String data) throws DataProcessingException {
    try {
        // Process data
    } catch (JsonParseException e) {
        throw new DataProcessingException(e); // Unnecessary try-catch
    }
}

// Better: Let it propagate
public void processData(String data) throws JsonParseException {
    // Process data
}

✅ 良好——能够处理时再捕获:

public void processDataWithRetry(String data) {
    int maxAttempts = 3;
    for (int attempt = 1; attempt <= maxAttempts; attempt++) {
        try {
            processData(data);
            return; // Success
        } catch (TransientException e) {
            if (attempt == maxAttempts) {
                log.error("Failed after {} attempts", maxAttempts, e);
                throw new DataProcessingException("Processing failed", e);
            }
            log.warn("Attempt {} failed, retrying...", attempt);
            sleep(1000 * attempt); // Exponential backoff
        }
    }
}
自定义异常设计

✅ 设计良好的自定义异常:

public class InsufficientFundsException extends Exception {
    private final double requestedAmount;
    private final double availableBalance;

    public InsufficientFundsException(double requestedAmount, double availableBalance) {
        super(String.format("Insufficient funds: requested %.2f, available %.2f",
            requestedAmount, availableBalance));
        this.requestedAmount = requestedAmount;
        this.availableBalance = availableBalance;
    }

    public double getRequestedAmount() {
        return requestedAmount;
    }

    public double getAvailableBalance() {
        return availableBalance;
    }

    public double getShortfall() {
        return requestedAmount - availableBalance;
    }
}

// Usage
try {
    account.withdraw(500);
} catch (InsufficientFundsException e) {
    log.error("Withdrawal failed: {}", e.getMessage());
    notifyUser(String.format("You need $%.2f more", e.getShortfall()));
}
记录异常日志

✅ 异常日志记录最佳实践:

public void processOrder(Order order) {
    try {
        validateOrder(order);
        saveOrder(order);
        sendConfirmation(order);
    } catch (ValidationException e) {
        // Log with context
        log.error("Order validation failed for order {}: {}",
            order.getId(), e.getMessage(), e);
        throw e;
    } catch (DataAccessException e) {
        // Log with different level based on recoverability
        log.error("Failed to save order {}", order.getId(), e);
        throw new OrderProcessingException("Failed to save order", e);
    } catch (EmailException e) {
        // Non-critical error - log warning
        log.warn("Failed to send confirmation email for order {}",
            order.getId(), e);
        // Don't rethrow - order was saved successfully
    }
}
切勿吞掉异常

❌ 不佳——吞掉异常:

try {
    riskyOperation();
} catch (Exception e) {
    // Silent failure - NEVER DO THIS!
}

try {
    closeResource();
} catch (Exception e) {
    e.printStackTrace(); // Insufficient - use logging!
}

✅ 良好——正确处理异常:

try {
    riskyOperation();
} catch (Exception e) {
    log.error("Operation failed", e);
    throw new ApplicationException("Failed to perform operation", e);
}

// Or if truly acceptable to ignore
try {
    closeResource();
} catch (IOException e) {
    log.warn("Failed to close resource (non-critical)", e);
    // OK to continue without rethrowing in cleanup scenarios
}

集合与泛型

选择合适的集合

集合选择指南:

// List - Ordered collection, allows duplicates
// Use ArrayList for random access, LinkedList for frequent insertions/deletions
List<String> names = new ArrayList<>();
List<Task> taskQueue = new LinkedList<>();

// Set - No duplicates, no guaranteed order
// Use HashSet for general use, TreeSet for sorted, LinkedHashSet for insertion order
Set<String> uniqueEmails = new HashSet<>();
Set<Integer> sortedNumbers = new TreeSet<>();
Set<String> insertionOrderSet = new LinkedHashSet<>();

// Map - Key-value pairs, no duplicate keys
// Use HashMap for general use, TreeMap for sorted keys, LinkedHashMap for insertion order
Map<Long, User> userCache = new HashMap<>();
Map<String, Integer> sortedMap = new TreeMap<>();
Map<String, String> orderedMap = new LinkedHashMap<>();

// Queue - FIFO operations
// Use LinkedList or ArrayDeque for general queue
Queue<Task> taskQueue = new LinkedList<>();
Deque<String> deque = new ArrayDeque<>();

// Stack operations - Use Deque instead of Stack class
Deque<String> stack = new ArrayDeque<>();
stack.push("item");
String item = stack.pop();

性能特征:

// ArrayList
// - Get by index: O(1)
// - Add at end: O(1) amortized
// - Insert/remove at position: O(n)
// - Search: O(n)

// LinkedList
// - Get by index: O(n)
// - Add/remove at beginning/end: O(1)
// - Insert/remove at position: O(n)
// - Search: O(n)

// HashSet/HashMap
// - Add/remove/contains: O(1) average
// - Iteration: O(capacity + size)

// TreeSet/TreeMap
// - Add/remove/contains: O(log n)
// - Iteration in sorted order: O(n)
不可变集合

✅ 创建不可变集合:

// Java 9+ factory methods (preferred)
List<String> immutableList = List.of("a", "b", "c");
Set<String> immutableSet = Set.of("x", "y", "z");
Map<String, Integer> immutableMap = Map.of(
    "one", 1,
    "two", 2,
    "three", 3
);

// For more than 10 entries in Map
Map<String, Integer> largeMap = Map.ofEntries(
    Map.entry("key1", 1),
    Map.entry("key2", 2),
    Map.entry("key3", 3)
);

// Pre-Java 9 - Collections.unmodifiableXxx
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
List<String> immutableList = Collections.unmodifiableList(list);

// Guava (if available)
ImmutableList<String> immutableList = ImmutableList.of("a", "b", "c");
ImmutableSet<String> immutableSet = ImmutableSet.of("x", "y", "z");
ImmutableMap<String, Integer> immutableMap = ImmutableMap.of("one", 1, "two", 2);
泛型类型安全

✅ 正确使用泛型:

// Generic class
public class Box<T> {
    private T content;

    public void set(T content) {
        this.content = content;
    }

    public T get() {
        return content;
    }
}

// Multiple type parameters
public class Pair<K, V> {
    private final K key;
    private final V value;

    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public K getKey() { return key; }
    public V getValue() { return value; }
}

// Bounded type parameters
public class NumberBox<T extends Number> {
    private T number;

    public void set(T number) {
        this.number = number;
    }

    public double getDoubleValue() {
        return number.doubleValue(); // Can call Number methods
    }
}

// Generic method
public <T> List<T> createList(T... elements) {
    return Arrays.asList(elements);
}

// Wildcard usage
public void processList(List<? extends Number> numbers) {
    for (Number num : numbers) {
        System.out.println(num.doubleValue());
    }
}

public void addToList(List<? super Integer> list) {
    list.add(42); // Can add Integer
}
菱形运算符用法

✅ 使用菱形运算符(Java 7+):

// Before Java 7
Map<String, List<String>> map = new HashMap<String, List<String>>();

// Java 7+ with diamond operator
Map<String, List<String>> map = new HashMap<>();

// Works with anonymous classes (Java 9+)
List<String> list = new ArrayList<>() {
    {
        add("item");
    }
};

并发

线程安全

**规则:**从一开始就为线程安全而设计。除非文档另有说明,否则应假设存在多线程访问。

✅ 线程安全模式:

// 1. Immutable objects (inherently thread-safe)
public final class ImmutableUser {
    private final String name;
    private final String email;

    public ImmutableUser(String name, String email) {
        this.name = name;
        this.email = email;
    }

    public String getName() { return name; }
    public String getEmail() { return email; }
}

// 2. Synchronized methods
public class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public synchronized int getCount() {
        return count;
    }
}

// 3. Concurrent collections
private final Map<String, User> userCache = new ConcurrentHashMap<>();
private final List<String> logMessages = new CopyOnWriteArrayList<>();

// 4. Atomic variables
private final AtomicInteger counter = new AtomicInteger(0);

public void incrementCounter() {
    counter.incrementAndGet();
}

public int getCounter() {
    return counter.get();
}

// 5. ThreadLocal for thread-specific data
private static final ThreadLocal<SimpleDateFormat> dateFormat =
    ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));

public String formatDate(Date date) {
    return dateFormat.get().format(date);
}
ExecutorService 用法

✅ 使用 ExecutorService:

// Fixed thread pool
ExecutorService executor = Executors.newFixedThreadPool(10);

// Submit tasks
for (int i = 0; i < 100; i++) {
    final int taskId = i;
    executor.submit(() -> {
        processTask(taskId);
    });
}

// Shutdown gracefully
executor.shutdown();
try {
    if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
        executor.shutdownNow();
    }
} catch (InterruptedException e) {
    executor.shutdownNow();
    Thread.currentThread().interrupt();
}

// Try-with-resources (Java 19+)
try (ExecutorService executor = Executors.newFixedThreadPool(10)) {
    // Submit tasks
} // Auto-shutdown

// Custom thread pool for better control
ExecutorService customExecutor = new ThreadPoolExecutor(
    5,  // core pool size
    10, // maximum pool size
    60L, TimeUnit.SECONDS, // keep alive time
    new LinkedBlockingQueue<>(100), // work queue
    new ThreadPoolExecutor.CallerRunsPolicy() // rejection policy
);
CompletableFuture 模式

✅ 使用 CompletableFuture 进行异步编程:

// Simple async task
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    // Long-running task
    return fetchDataFromApi();
});

// Chain operations
CompletableFuture<UserDto> userFuture = CompletableFuture
    .supplyAsync(() -> fetchUserFromDatabase(userId))
    .thenApply(user -> mapToDto(user))
    .thenApply(dto -> enrichWithAdditionalData(dto));

// Combine multiple futures
CompletableFuture<User> userFuture = fetchUserAsync(userId);
CompletableFuture<List<Order>> ordersFuture = fetchOrdersAsync(userId);

CompletableFuture<UserWithOrders> combined = userFuture
    .thenCombine(ordersFuture, (user, orders) ->
        new UserWithOrders(user, orders));

// Handle errors
CompletableFuture<String> result = CompletableFuture
    .supplyAsync(() -> riskyOperation())
    .exceptionally(ex -> {
        log.error("Operation failed", ex);
        return "default value";
    });

// Multiple async operations
List<CompletableFuture<String>> futures = ids.stream()
    .map(id -> CompletableFuture.supplyAsync(() -> fetchData(id)))
    .collect(Collectors.toList());

// Wait for all to complete
CompletableFuture<Void> allOf = CompletableFuture.allOf(
    futures.toArray(new CompletableFuture[0]));

allOf.thenRun(() -> {
    List<String> results = futures.stream()
        .map(CompletableFuture::join)
        .collect(Collectors.toList());
    processResults(results);
});
避免死锁

✅ 预防死锁:

// 1. Always acquire locks in the same order
public class BankAccount {
    private final Object lock = new Object();
    private double balance;

    public static void transfer(BankAccount from, BankAccount to, double amount) {
        // Always lock accounts in consistent order (by hashCode)
        BankAccount first = from.hashCode() < to.hashCode() ? from : to;
        BankAccount second = from.hashCode() < to.hashCode() ? to : from;

        synchronized (first.lock) {
            synchronized (second.lock) {
                if (from == first) {
                    from.withdraw(amount);
                    to.deposit(amount);
                } else {
                    from.deposit(amount);
                    to.withdraw(amount);
                }
            }
        }
    }
}

// 2. Use tryLock with timeout
Lock lock1 = new ReentrantLock();
Lock lock2 = new ReentrantLock();

try {
    if (lock1.tryLock(1, TimeUnit.SECONDS)) {
        try {
            if (lock2.tryLock(1, TimeUnit.SECONDS)) {
                try {
                    // Critical section
                } finally {
                    lock2.unlock();
                }
            }
        } finally {
            lock1.unlock();
        }
    }
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
}

// 3. Use higher-level concurrency utilities
ConcurrentHashMap<String, Account> accounts = new ConcurrentHashMap<>();
accounts.compute("account1", (key, account) -> {
    account.withdraw(100);
    return account;
});
原子类

✅ 使用原子类:

public class Statistics {
    private final AtomicLong totalRequests = new AtomicLong(0);
    private final AtomicLong successfulRequests = new AtomicLong(0);
    private final AtomicLong failedRequests = new AtomicLong(0);

    public void recordSuccess() {
        totalRequests.incrementAndGet();
        successfulRequests.incrementAndGet();
    }

    public void recordFailure() {
        totalRequests.incrementAndGet();
        failedRequests.incrementAndGet();
    }

    public double getSuccessRate() {
        long total = totalRequests.get();
        if (total == 0) return 0.0;
        return (double) successfulRequests.get() / total;
    }

    // Atomic update with compareAndSet
    private final AtomicReference<Config> config =
        new AtomicReference<>(new Config());

    public void updateConfig(Config newConfig) {
        Config current;
        do {
            current = config.get();
        } while (!config.compareAndSet(current, newConfig));
    }
}

测试(TDD)

使用 JUnit 5 进行单元测试

✅ JUnit 5 最佳实践:

@DisplayName("User Service Tests")
class UserServiceTest {

    private UserService userService;
    private UserRepository userRepository;
    private PasswordEncoder passwordEncoder;

    @BeforeEach
    void setUp() {
        userRepository = mock(UserRepository.class);
        passwordEncoder = mock(PasswordEncoder.class);
        userService = new UserService(userRepository, passwordEncoder);
    }

    @Test
    @DisplayName("Should create user with valid data")
    void shouldCreateUserWithValidData() {
        // Arrange
        String email = "[email protected]";
        String password = "SecurePass123!";
        String encodedPassword = "encoded_password";

        when(passwordEncoder.encode(password)).thenReturn(encodedPassword);
        when(userRepository.existsByEmail(email)).thenReturn(false);

        // Act
        User user = userService.createUser(email, password);

        // Assert
        assertNotNull(user);
        assertEquals(email, user.getEmail());
        assertEquals(encodedPassword, user.getPasswordHash());
        verify(userRepository).save(any(User.class));
    }

    @Test
    @DisplayName("Should throw exception when email already exists")
    void shouldThrowExceptionWhenEmailExists() {
        // Arrange
        String email = "[email protected]";
        when(userRepository.existsByEmail(email)).thenReturn(true);

        // Act & Assert
        assertThrows(DuplicateEmailException.class, () ->
            userService.createUser(email, "password"));

        verify(userRepository, never()).save(any());
    }

    @ParameterizedTest
    @ValueSource(strings = {"", "  ", "invalid-email", "@example.com"})
    @DisplayName("Should throw exception for invalid email formats")
    void shouldThrowExceptionForInvalidEmail(String invalidEmail) {
        assertThrows(ValidationException.class, () ->
            userService.createUser(invalidEmail, "password"));
    }

    @ParameterizedTest
    @CsvSource({
        "[email protected], short, 'Password too short'",
        "[email protected], nodigits, 'Password must contain digits'",
        "invalid, ValidPass123!, 'Invalid email format'"
    })
    @DisplayName("Should validate user input correctly")
    void shouldValidateUserInput(String email, String password, String expectedError) {
        ValidationException exception = assertThrows(ValidationException.class, () ->
            userService.createUser(email, password));

        assertTrue(exception.getMessage().contains(expectedError));
    }

    @Test
    @DisplayName("Should handle repository exception gracefully")
    void shouldHandleRepositoryException() {
        // Arrange
        when(userRepository.save(any())).thenThrow(new DataAccessException("DB error"));

        // Act & Assert
        assertThrows(UserCreationException.class, () ->
            userService.createUser("[email protected]", "password"));
    }

    @Nested
    @DisplayName("User Authentication Tests")
    class UserAuthenticationTests {

        @Test
        @DisplayName("Should authenticate user with correct password")
        void shouldAuthenticateWithCorrectPassword() {
            // Arrange
            User user = new User("[email protected]", "encoded_pass");
            when(userRepository.findByEmail("[email protected]"))
                .thenReturn(Optional.of(user));
            when(passwordEncoder.matches("password", "encoded_pass"))
                .thenReturn(true);

            // Act
            boolean authenticated = userService.authenticate("[email protected]", "password");

            // Assert
            assertTrue(authenticated);
        }

        @Test
        @DisplayName("Should reject authentication with wrong password")
        void shouldRejectWithWrongPassword() {
            // Arrange
            User user = new User("[email protected]", "encoded_pass");
            when(userRepository.findByEmail("[email protected]"))
                .thenReturn(Optional.of(user));
            when(passwordEncoder.matches("wrong", "encoded_pass"))
                .thenReturn(false);

            // Act
            boolean authenticated = userService.authenticate("[email protected]", "wrong");

            // Assert
            assertFalse(authenticated);
        }
    }
}
使用 Mockito 进行模拟

✅ Mockito 最佳实践:

public class OrderServiceTest {

    @Mock
    private OrderRepository orderRepository;

    @Mock
    private PaymentService paymentService;

    @Mock
    private EmailService emailService;

    @InjectMocks
    private OrderService orderService;

    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
    }

    @Test
    void shouldProcessOrderSuccessfully() {
        // Arrange
        Order order = new Order();
        order.setId(1L);
        order.setTotal(100.0);

        when(paymentService.charge(any(), anyDouble())).thenReturn(true);
        when(orderRepository.save(any(Order.class))).thenReturn(order);

        // Act
        orderService.processOrder(order);

        // Assert
        verify(paymentService).charge(order, 100.0);
        verify(orderRepository).save(order);
        verify(emailService).sendConfirmation(order);

        assertEquals(OrderStatus.COMPLETED, order.getStatus());
    }

    @Test
    void shouldHandlePaymentFailure() {
        // Arrange
        Order order = new Order();
        when(paymentService.charge(any(), anyDouble())).thenReturn(false);

        // Act & Assert
        assertThrows(PaymentFailedException.class, () ->
            orderService.processOrder(order));

        verify(emailService, never()).sendConfirmation(any());
        assertEquals(OrderStatus.PAYMENT_FAILED, order.getStatus());
    }

    @Test
    void shouldRetryOnTransientFailure() {
        // Arrange
        Order order = new Order();

        // First two calls fail, third succeeds
        when(paymentService.charge(any(), anyDouble()))
            .thenThrow(new TransientException())
            .thenThrow(new TransientException())
            .thenReturn(true);

        // Act
        orderService.processOrderWithRetry(order);

        // Assert
        verify(paymentService, times(3)).charge(any(), anyDouble());
    }

    @Test
    void shouldCaptureArgumentValues() {
        // Arrange
        ArgumentCaptor<Order> orderCaptor = ArgumentCaptor.forClass(Order.class);
        Order order = new Order();
        order.setTotal(100.0);

        // Act
        orderService.processOrder(order);

        // Assert
        verify(orderRepository).save(orderCaptor.capture());
        Order savedOrder = orderCaptor.getValue();
        assertEquals(OrderStatus.COMPLETED, savedOrder.getStatus());
        assertNotNull(savedOrder.getProcessedAt());
    }
}
测试组织

包结构:

src/
├── main/
│   └── java/
│       └── com/example/
│           ├── model/
│           │   └── User.java
│           └── service/
│               └── UserService.java
└── test/
    └── java/
        └── com/example/
            ├── model/
            │   └── UserTest.java
            └── service/
                └── UserServiceTest.java
AAA 模式(准备-执行-断言)

✅ 清晰的测试结构:

@Test
void shouldCalculateDiscountCorrectly() {
    // Arrange - Set up test data and expectations
    Product product = new Product("Laptop", 1000.0);
    Discount discount = new Discount(10); // 10%
    PriceCalculator calculator = new PriceCalculator();

    // Act - Execute the behavior being tested
    double finalPrice = calculator.calculateFinalPrice(product, discount);

    // Assert - Verify the expected outcome
    assertEquals(900.0, finalPrice, 0.01);
}
测试私有方法

**规则:**不要直接测试私有方法。应通过公有方法进行测试。

❌ 不佳:

@Test
void testPrivateMethod() {
    // Using reflection to test private method - BAD!
    Method method = UserService.class.getDeclaredMethod("validateEmail", String.class);
    method.setAccessible(true);
    boolean result = (boolean) method.invoke(userService, "[email protected]");
    assertTrue(result);
}

✅ 良好:

@Test
void shouldRejectInvalidEmailDuringUserCreation() {
    // Test private validateEmail() through public createUser()
    assertThrows(ValidationException.class, () ->
        userService.createUser("invalid-email", "password"));
}
测试覆盖率目标

推荐的覆盖率目标:

  • 关键业务逻辑:100%
  • 服务层:90-100%
  • 控制器层:80-90%
  • 工具类:90-100%
  • 整个项目:至少 80%

工具:

  • JaCoCo 用于覆盖率报告
  • SonarQube 用于代码质量分析

<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.10</version>
    <executions>
        <execution>
            <goals>
                <goal>prepare-agent</goal>
            </goals>
        </execution>
        <execution>
            <id>report</id>
            <phase>test</phase>
            <goals>
                <goal>report</goal>
            </goals>
        </execution>
        <execution>
            <id>coverage-check</id>
            <goals>
                <goal>check</goal>
            </goals>
            <configuration>
                <rules>
                    <rule>
                        <element>PACKAGE</element>
                        <limits>
                            <limit>
                                <counter>LINE</counter>
                                <value>COVEREDRATIO</value>
                                <minimum>0.80</minimum>
                            </limit>
                        </limits>
                    </rule>
                </rules>
            </configuration>
        </execution>
    </executions>
</plugin>

代码组织

包结构

✅ 分层架构:

com.example.myapp/
├── controller/          # REST controllers
│   ├── UserController.java
│   └── OrderController.java
├── service/            # Business logic
│   ├── UserService.java
│   └── OrderService.java
├── repository/         # Data access
│   ├── UserRepository.java
│   └── OrderRepository.java
├── model/             # Domain models
│   ├── User.java
│   └── Order.java
├── dto/               # Data transfer objects
│   ├── UserDto.java
│   └── OrderDto.java
├── exception/         # Custom exceptions
│   ├── UserNotFoundException.java
│   └── ValidationException.java
├── config/           # Configuration classes
│   ├── SecurityConfig.java
│   └── DatabaseConfig.java
└── util/            # Utility classes
    ├── DateUtils.java
    └── StringUtils.java

✅ 按功能划分的结构(适用于较大型应用):

com.example.myapp/
├── user/
│   ├── User.java
│   ├── UserController.java
│   ├── UserService.java
│   ├── UserRepository.java
│   └── UserDto.java
├── order/
│   ├── Order.java
│   ├── OrderController.java
│   ├── OrderService.java
│   ├── OrderRepository.java
│   └── OrderDto.java
└── common/
    ├── exception/
    ├── config/
    └── util/
类命名约定
// Controllers: Noun + Controller
public class UserController { }
public class OrderController { }

// Services: Noun + Service
public class UserService { }
public class PaymentService { }

// Repositories: Noun + Repository
public class UserRepository { }
public class OrderRepository { }

// DTOs: Noun + Dto
public class UserDto { }
public class CreateOrderDto { }

// Exceptions: Description + Exception
public class UserNotFoundException extends RuntimeException { }
public class InvalidEmailException extends ValidationException { }

// Utilities: Plural noun + Utils
public class StringUtils { }
public class DateUtils { }

// Interfaces: Adjective or Noun
public interface Serializable { }
public interface PaymentProcessor { }
方法命名约定
// Getters/Setters
public String getName() { }
public void setName(String name) { }

// Boolean getters: is/has/can
public boolean isActive() { }
public boolean hasPermission() { }
public boolean canExecute() { }

// Actions: verb + noun
public void createUser() { }
public void processPayment() { }
public void calculateTotal() { }

// Queries: get/find/search + noun
public User getUser(Long id) { }
public List<User> findActiveUsers() { }
public List<Order> searchByDate(LocalDate date) { }

// Validators: validate + noun
public void validateEmail(String email) { }
public boolean isValidPassword(String password) { }
常量与配置

✅ 常量:

public class ApplicationConstants {
    // Public constants: public static final
    public static final int MAX_RETRY_ATTEMPTS = 3;
    public static final String DEFAULT_ENCODING = "UTF-8";
    public static final Duration CONNECTION_TIMEOUT = Duration.ofSeconds(30);

    // Private constructor to prevent instantiation
    private ApplicationConstants() {
        throw new AssertionError("Cannot instantiate constants class");
    }
}

// Or use enums for related constants
public enum HttpStatus {
    OK(200, "OK"),
    NOT_FOUND(404, "Not Found"),
    INTERNAL_SERVER_ERROR(500, "Internal Server Error");

    private final int code;
    private final String message;

    HttpStatus(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public int getCode() { return code; }
    public String getMessage() { return message; }
}
建造者模式

✅ 为复杂对象使用建造者:

public class User {
    private final String email;
    private final String firstName;
    private final String lastName;
    private final LocalDate dateOfBirth;
    private final String phoneNumber;
    private final Address address;

    private User(Builder builder) {
        this.email = builder.email;
        this.firstName = builder.firstName;
        this.lastName = builder.lastName;
        this.dateOfBirth = builder.dateOfBirth;
        this.phoneNumber = builder.phoneNumber;
        this.address = builder.address;
    }

    public static Builder builder() {
        return new Builder();
    }

    public static class Builder {
        private String email;
        private String firstName;
        private String lastName;
        private LocalDate dateOfBirth;
        private String phoneNumber;
        private Address address;

        public Builder email(String email) {
            this.email = email;
            return this;
        }

        public Builder firstName(String firstName) {
            this.firstName = firstName;
            return this;
        }

        public Builder lastName(String lastName) {
            this.lastName = lastName;
            return this;
        }

        public Builder dateOfBirth(LocalDate dateOfBirth) {
            this.dateOfBirth = dateOfBirth;
            return this;
        }

        public Builder phoneNumber(String phoneNumber) {
            this.phoneNumber = phoneNumber;
            return this;
        }

        public Builder address(Address address) {
            this.address = address;
            return this;
        }

        public User build() {
            validateRequired();
            return new User(this);
        }

        private void validateRequired() {
            if (email == null) throw new IllegalStateException("Email is required");
            if (firstName == null) throw new IllegalStateException("First name is required");
        }
    }

    // Getters
    public String getEmail() { return email; }
    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }
    public LocalDate getDateOfBirth() { return dateOfBirth; }
    public String getPhoneNumber() { return phoneNumber; }
    public Address getAddress() { return address; }
}

// Usage
User user = User.builder()
    .email("[email protected]")
    .firstName("John")
    .lastName("Doe")
    .dateOfBirth(LocalDate.of(1990, 1, 1))
    .phoneNumber("555-1234")
    .build();
工厂模式

✅ 使用工厂创建对象:

public interface PaymentProcessor {
    void processPayment(double amount);
}

public class CreditCardProcessor implements PaymentProcessor {
    @Override
    public void processPayment(double amount) {
        System.out.println("Processing credit card payment: $" + amount);
    }
}

public class PayPalProcessor implements PaymentProcessor {
    @Override
    public void processPayment(double amount) {
        System.out.println("Processing PayPal payment: $" + amount);
    }
}

public class PaymentProcessorFactory {
    public static PaymentProcessor create(PaymentMethod method) {
        switch (method) {
            case CREDIT_CARD:
                return new CreditCardProcessor();
            case PAYPAL:
                return new PayPalProcessor();
            case BITCOIN:
                return new BitcoinProcessor();
            default:
                throw new IllegalArgumentException("Unknown payment method: " + method);
        }
    }
}

// Usage
PaymentProcessor processor = PaymentProcessorFactory.create(PaymentMethod.CREDIT_CARD);
processor.processPayment(100.0);

性能

字符串优化

✅ 字符串性能提示:

// Use String.format() sparingly (it's slow)
// OK for occasional use
String message = String.format("User %s logged in at %s", username, timestamp);

// For frequent formatting, use StringBuilder
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    builder.append("Item ").append(i).append("\n");
}
String result = builder.toString();

// Use String.join() for collections
List<String> items = Arrays.asList("apple", "banana", "cherry");
String joined = String.join(", ", items);

// Avoid string concatenation in loops
String result = "";
for (String item : items) {
    result += item; // BAD - creates many String objects
}

// Use StringBuilder instead
StringBuilder result = new StringBuilder();
for (String item : items) {
    result.append(item);
}

// String interning for frequently used strings
String s1 = new String("hello").intern();
String s2 = "hello";
assert s1 == s2; // Same reference
集合性能
// ArrayList vs LinkedList
List<String> arrayList = new ArrayList<>(); // Fast random access O(1)
List<String> linkedList = new LinkedList<>(); // Fast insertion/deletion at ends O(1)

// HashSet vs TreeSet
Set<String> hashSet = new HashSet<>(); // O(1) add/contains, no order
Set<String> treeSet = new TreeSet<>(); // O(log n) add/contains, sorted

// HashMap vs TreeMap vs LinkedHashMap
Map<String, Integer> hashMap = new HashMap<>(); // O(1) get/put, no order
Map<String, Integer> treeMap = new TreeMap<>(); // O(log n) get/put, sorted by key
Map<String, Integer> linkedHashMap = new LinkedHashMap<>(); // O(1) get/put, insertion order

// Pre-size collections when size is known
List<String> list = new ArrayList<>(1000); // Avoids resizing
Map<String, String> map = new HashMap<>(1000);

// Use EnumSet for enum values
Set<DayOfWeek> weekdays = EnumSet.of(
    DayOfWeek.MONDAY, DayOfWeek.TUESDAY, DayOfWeek.WEDNESDAY,
    DayOfWeek.THURSDAY, DayOfWeek.FRIDAY
);
Stream 与循环的性能

**规则:**Stream 可读性好,但用于小型集合时可能会产生开销。对于关键路径,应测量性能。

// For small collections (<100 items), loops may be faster
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// Loop (slightly faster for small collections)
int sum = 0;
for (int num : numbers) {
    sum += num;
}

// Stream (more readable, negligible overhead)
int sum = numbers.stream()
    .mapToInt(Integer::intValue)
    .sum();

// For large collections or parallel processing, streams shine
List<Integer> largeList = IntStream.range(0, 1_000_000)
    .boxed()
    .collect(Collectors.toList());

// Parallel stream for CPU-intensive operations
int sum = largeList.parallelStream()
    .mapToInt(Integer::intValue)
    .sum();
内存管理

✅ 内存最佳实践:

// Close resources to free memory
try (FileInputStream fis = new FileInputStream("file.txt");
     BufferedInputStream bis = new BufferedInputStream(fis)) {
    // Use streams
} // Automatically closed

// Avoid memory leaks with listeners
public class EventSource {
    private final List<EventListener> listeners = new ArrayList<>();

    public void addListener(EventListener listener) {
        listeners.add(listener);
    }

    public void removeListener(EventListener listener) {
        listeners.remove(listener); // Important: allow garbage collection
    }
}

// Use weak references for caches
Map<String, WeakReference<User>> cache = new WeakHashMap<>();

// Clear collections when done
List<LargeObject> objects = new ArrayList<>();
// ... use objects
objects.clear(); // Allow garbage collection

// Avoid string concatenation creating many objects
String result = "Hello" + " " + "World"; // OK - compiler optimizes
String result = str1 + str2 + str3; // OK - compiler uses StringBuilder

// Not OK in loops - use StringBuilder explicitly
for (String item : items) {
    result = result + item; // Creates new String each iteration
}

常见反模式

上帝对象

❌ 反模式——上帝对象:

// One class doing everything - AVOID!
public class OrderManager {
    // User management
    public void createUser() { }
    public void deleteUser() { }

    // Order processing
    public void createOrder() { }
    public void processOrder() { }

    // Payment processing
    public void processPayment() { }

    // Email sending
    public void sendConfirmation() { }

    // Reporting
    public void generateReport() { }

    // Database operations
    public void saveToDatabase() { }

    // Logging
    public void logActivity() { }

    // 50+ more methods...
}

✅ 解决方案——单一职责:

public class UserService {
    public void createUser() { }
    public void deleteUser() { }
}

public class OrderService {
    public void createOrder() { }
    public void processOrder() { }
}

public class PaymentService {
    public void processPayment() { }
}

public class EmailService {
    public void sendConfirmation() { }
}
基本类型偏执

❌ 反模式——基本类型偏执:

public class Order {
    private double price; // What currency?
    private double weight; // What unit?
    private String phoneNumber; // No validation!
    private String email; // No validation!
}

✅ 解决方案——值对象:

public class Money {
    private final double amount;
    private final Currency currency;

    public Money(double amount, Currency currency) {
        this.amount = amount;
        this.currency = currency;
    }
    // Methods for money operations
}

public class Weight {
    private final double value;
    private final WeightUnit unit;

    public Weight(double value, WeightUnit unit) {
        this.value = value;
        this.unit = unit;
    }
}

public class Email {
    private final String value;

    public Email(String value) {
        if (!isValid(value)) {
            throw new IllegalArgumentException("Invalid email");
        }
        this.value = value;
    }

    private boolean isValid(String email) {
        return email.contains("@"); // Simplified
    }
}

public class Order {
    private Money price;
    private Weight weight;
    private Email customerEmail;
}
参数列表过长

❌ 反模式 - 参数列表过长:

public void createUser(
    String firstName,
    String lastName,
    String email,
    String phoneNumber,
    String addressLine1,
    String addressLine2,
    String city,
    String state,
    String zipCode,
    String country
) {
    // Too many parameters!
}

✅ 解决方案 - 参数对象:

public class UserRegistration {
    private final String firstName;
    private final String lastName;
    private final String email;
    private final String phoneNumber;
    private final Address address;

    // Constructor, getters, builder
}

public class Address {
    private final String line1;
    private final String line2;
    private final String city;
    private final String state;
    private final String zipCode;
    private final String country;

    // Constructor, getters, builder
}

public void createUser(UserRegistration registration) {
    // Clean method signature
}
魔法数字

❌ 反模式 - 魔法数字:

public void processOrder(Order order) {
    if (order.getTotal() > 100) { // What is 100?
        applyDiscount(order, 0.1); // What is 0.1?
    }

    if (order.getItems().size() > 5) { // What is 5?
        // Free shipping
    }
}

✅ 解决方案 - 命名常量:

private static final double FREE_SHIPPING_THRESHOLD = 100.0;
private static final double BULK_DISCOUNT_RATE = 0.1;
private static final int BULK_ORDER_ITEM_COUNT = 5;

public void processOrder(Order order) {
    if (order.getTotal() > FREE_SHIPPING_THRESHOLD) {
        applyDiscount(order, BULK_DISCOUNT_RATE);
    }

    if (order.getItems().size() > BULK_ORDER_ITEM_COUNT) {
        // Free shipping
    }
}
嵌套条件语句

❌ 反模式 - 条件语句嵌套过深:

public void processPayment(Order order) {
    if (order != null) {
        if (order.getCustomer() != null) {
            if (order.getCustomer().hasPaymentMethod()) {
                if (order.getTotal() > 0) {
                    if (inventory.isAvailable(order)) {
                        // Process payment
                    } else {
                        throw new InsufficientInventoryException();
                    }
                } else {
                    throw new InvalidAmountException();
                }
            } else {
                throw new NoPaymentMethodException();
            }
        } else {
            throw new NoCustomerException();
        }
    } else {
        throw new NullOrderException();
    }
}

✅ 解决方案 - 守卫子句:

public void processPayment(Order order) {
    // Guard clauses - fail fast
    if (order == null) {
        throw new NullOrderException();
    }
    if (order.getCustomer() == null) {
        throw new NoCustomerException();
    }
    if (!order.getCustomer().hasPaymentMethod()) {
        throw new NoPaymentMethodException();
    }
    if (order.getTotal() <= 0) {
        throw new InvalidAmountException();
    }
    if (!inventory.isAvailable(order)) {
        throw new InsufficientInventoryException();
    }

    // Happy path at the end
    processPaymentInternal(order);
}
返回 Null

❌ 反模式 - 返回 Null:

public User findUser(Long id) {
    // May return null
    return database.find(id);
}

// Caller must remember null check
User user = findUser(123L);
if (user != null) {
    String email = user.getEmail(); // NullPointerException risk
}

✅ 解决方案 - 返回 Optional:

public Optional<User> findUser(Long id) {
    return Optional.ofNullable(database.find(id));
}

// Caller forced to handle absence
Optional<User> userOpt = findUser(123L);
String email = userOpt
    .map(User::getEmail)
    .orElse("[email protected]");
未关闭资源

❌ 反模式 - 资源泄漏:

public String readFile(String path) {
    FileReader reader = new FileReader(path);
    BufferedReader br = new BufferedReader(reader);
    return br.readLine(); // Resources never closed!
}

✅ 解决方案 - Try-With-Resources:

public String readFile(String path) throws IOException {
    try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
        return reader.readLine();
    } // Automatically closed
}

检查清单

在代码审查期间使用此检查清单,验证 Java 代码质量:

SOLID 原则
  • 每个类都有单一且定义明确的职责
  • 类对扩展开放,对修改关闭
  • 子类可以替代基类
  • 接口精简且专注
  • 依赖关系已反转(依赖抽象)
DRY 原则
  • 没有明显的代码重复
  • 将通用逻辑提取到实用方法/类中
  • 在适当情况下使用泛型
整洁代码
  • 类、方法和变量名称具有明确含义
  • 方法体量较小(理想情况下为 5-20 行)
  • 注释解释原因,而非内容
  • 异常处理得当(不吞掉异常)
  • 代码组织合理
Java 特有事项
  • 对于缺失值,使用 Optional 而非 null
  • 优先使用组合而非继承
  • 在适当情况下使用不可变性(final 字段/类)
  • 使用 Stream API 执行集合操作
  • 使用 Lambda 表达式和方法引用
  • 对 AutoCloseable 资源使用 Try-With-Resources
  • 在循环中拼接字符串时使用 StringBuilder
  • 对固定常量集合使用枚举
异常处理
  • 使用适当的异常类型(受检异常与非受检异常)
  • 仅在能够处理异常时捕获异常
  • 自定义异常设计合理
  • 记录异常时附带上下文信息
  • 没有空的 catch 块
集合
  • 选择了合适的集合类型
  • 在适当情况下使用不可变集合
  • 指定泛型类型参数
  • 使用菱形运算符(Java 7+)
并发
  • 考虑了线程安全
  • 使用了适当的同步机制
  • 使用 ExecutorService 管理线程池
  • 使用 CompletableFuture 执行异步操作
  • 考虑了死锁预防
  • 对简单的原子操作使用原子类
测试
  • 为业务逻辑编写了单元测试
  • 测试遵循 AAA 模式
  • 适当地使用模拟
  • 测试相互隔离且独立
  • 测试覆盖率符合标准(80%+)
代码组织
  • 包结构合理
  • 遵循命名约定
  • 正确定义常量
  • 适当地使用 Builder/Factory 模式
性能
  • 优化了字符串操作
  • 根据使用场景选择合适的集合类型
  • 正确管理资源
  • 没有明显的性能问题
反模式
  • 没有上帝对象
  • 没有基本类型偏执
  • 没有过长的参数列表
  • 没有魔法数字
  • 没有嵌套过深的条件语句
  • 在适合使用 Optional 的情况下不返回 null
  • 没有资源泄漏

相关技能

  • uncle-duke-java:将此技能用作参考的 Java 代码审查 Agent

参考资料

Java 文档
Spring 框架
Best Practices
Testing
Tools

Version: 1.0 Last Updated: 2025-12-24 Maintainer: Development Team

评论

支持 Markdown