重走Java Day07:抽象、接口与内部类——从“语法理解”到“架构思维”的觉醒

我曾以为理解了抽象类和接口的语法,就掌握了面向对象的高级特性。直到我在一个真实项目中,因为滥用抽象类导致系统重构,才明白:抽象不是语法糖,而是设计决策

开篇:那个让我重构整个支付系统的“抽象失误”

一年前,我负责设计一个电商平台的支付模块。需求看起来很简单:支持微信支付、支付宝、银联支付,未来可能扩展其他支付方式。

我自信地设计了第一版:

java

// 第一版:自以为“优雅”的抽象类设计
abstract class Payment {
    protected String paymentId;
    protected BigDecimal amount;
    protected PaymentStatus status;
    
    // 通用方法
    public void validate() {
        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
            throw new IllegalArgumentException("金额必须大于0");
        }
    }
    
    public void logPayment() {
        System.out.println("记录支付日志: " + paymentId);
    }
    
    // 抽象方法:每种支付方式不同的实现
    public abstract boolean process();
    public abstract boolean refund();
    public abstract PaymentStatus checkStatus();
}
​
// 具体实现
class WechatPayment extends Payment {
    private String openId;
    private String prepayId;
    
    @Override
    public boolean process() {
        // 微信支付特定逻辑
        return callWechatAPI();
    }
    
    @Override
    public boolean refund() {
        // 微信退款逻辑
        return callWechatRefundAPI();
    }
    
    // 问题:微信特有的方法混入了
    public String getPaymentQRCode() {
        return generateQRCode();
    }
}
​
class AlipayPayment extends Payment {
    private String tradeNo;
    private String buyerId;
    
    @Override
    public boolean process() {
        // 支付宝特定逻辑
        return callAlipayAPI();
    }
    
    // 问题:支付宝特有的回调处理
    public void handleAsyncNotify(String notifyData) {
        // 处理异步通知
    }
}

这个设计在第一次需求变更时就崩溃了:我们需要增加一个“组合支付”(同时使用余额+信用卡)。组合支付不是一个具体的支付方式,而是多种支付方式的组合。

image-20260201212224688

更糟的是,当我尝试引入“支付策略模式”时,发现抽象类限制了灵活性。我不得不重构整个支付模块

一、抽象类:从“半成品模板”到“设计契约”

1. 抽象类的真正价值:模板方法模式

用户资料提到了模板方法模式,但没深入解释为什么这个模式如此重要。在我重构支付系统时,我重新理解了抽象类:

java

// 重构后:专注于“固定流程”的抽象类
public abstract class AbstractPaymentProcessor {
    // 模板方法:定义支付处理的固定流程(final防止子类破坏)
    public final PaymentResult process(PaymentRequest request) {
        // 1. 验证(所有支付方式都一样)
        validateRequest(request);
        
        // 2. 前置处理(可被子类扩展)
        beforeProcess(request);
        
        // 3. 核心处理(子类必须实现)
        PaymentResult result = doProcess(request);
        
        // 4. 后置处理(可被子类扩展)
        afterProcess(request, result);
        
        // 5. 记录(所有支付方式都一样)
        recordPayment(request, result);
        
        return result;
    }
    
    // 固定实现的方法
    private void validateRequest(PaymentRequest request) {
        if (request.getAmount() == null || request.getAmount().signum() <= 0) {
            throw new ValidationException("支付金额无效");
        }
        // 更多通用验证...
    }
    
    private void recordPayment(PaymentRequest request, PaymentResult result) {
        // 记录到数据库
        paymentRepository.save(new PaymentRecord(request, result));
    }
    
    // 钩子方法:子类可以覆盖(但不是必须)
    protected void beforeProcess(PaymentRequest request) {
        // 默认空实现
    }
    
    protected void afterProcess(PaymentRequest request, PaymentResult result) {
        // 默认空实现
    }
    
    // 抽象方法:子类必须实现
    protected abstract PaymentResult doProcess(PaymentRequest request);
    
    // 另一个模板方法:退款流程
    public final RefundResult refund(RefundRequest request) {
        validateRefundRequest(request);
        RefundResult result = doRefund(request);
        recordRefund(request, result);
        return result;
    }
    
    protected abstract RefundResult doRefund(RefundRequest request);
}
​
// 具体实现变得非常干净
public class WechatPaymentProcessor extends AbstractPaymentProcessor {
    private final WechatPayClient wechatClient;
    
    @Override
    protected PaymentResult doProcess(PaymentRequest request) {
        // 只关注微信支付特有的逻辑
        WechatPaymentRequest wechatRequest = convertToWechatRequest(request);
        WechatPaymentResponse response = wechatClient.pay(wechatRequest);
        return convertToPaymentResult(response);
    }
    
    @Override
    protected RefundResult doRefund(RefundRequest request) {
        // 微信退款特有逻辑
        return wechatClient.refund(request);
    }
    
    @Override
    protected void beforeProcess(PaymentRequest request) {
        // 微信支付特有的前置处理
        String openId = getUserOpenId(request.getUserId());
        request.addParam("openId", openId);
    }
}

关键洞察:抽象类最适合的场景是定义算法的骨架,让子类在不改变算法结构的情况下重新定义算法的某些步骤。

2. 抽象类的构造器之谜

一个常被忽略但重要的点:抽象类可以有构造器,但不能被直接调用:

java

public abstract class BaseEntity {
    protected final String id;
    protected final Instant createdAt;
    protected Instant updatedAt;
    
    // 抽象类的构造器:初始化公共字段
    protected BaseEntity() {
        this.id = UUID.randomUUID().toString();
        this.createdAt = Instant.now();
        this.updatedAt = this.createdAt;
    }
    
    protected BaseEntity(String id, Instant createdAt) {
        this.id = id;
        this.createdAt = createdAt;
        this.updatedAt = createdAt;
    }
    
    // 模板方法:更新实体的固定流程
    public final void update() {
        validateBeforeUpdate();
        doUpdate();
        updatedAt = Instant.now();
        postUpdate();
    }
    
    protected abstract void doUpdate();
    protected void validateBeforeUpdate() { /* 默认实现 */ }
    protected void postUpdate() { /* 默认实现 */ }
}
​
// 子类构造器必须调用父类构造器
public class User extends BaseEntity {
    private String username;
    private String email;
    
    public User(String username, String email) {
        super();  // 调用父类无参构造器
        this.username = username;
        this.email = email;
    }
    
    @Override
    protected void doUpdate() {
        // 用户特定的更新逻辑
        validateEmail(email);
    }
    
    @Override
    protected void validateBeforeUpdate() {
        if (username == null || username.trim().isEmpty()) {
            throw new ValidationException("用户名不能为空");
        }
    }
}

3. 抽象类的“不可实例化”陷阱

我曾经犯过一个错误:试图绕过抽象类的实例化限制:

java

// ❌ 错误做法:试图“曲线救国”
abstract class ConfigLoader {
    public abstract Config load();
    
    // 提供一个静态工厂方法
    public static ConfigLoader createDefault() {
        // 返回一个匿名内部类实例
        return new ConfigLoader() {
            @Override
            public Config load() {
                return loadFromDefaultPath();
            }
        };
    }
}
​
// 问题:这破坏了抽象类的设计意图
// 抽象类应该是“需要被继承才有意义”的
// 如果可以创建实例,就应该用普通类+接口
​
// ✅ 正确做法:明确设计意图
// 情况1:真的需要实例 -> 用普通类
class DefaultConfigLoader {
    public Config load() {
        return loadFromDefaultPath();
    }
}
​
// 情况2:需要多态 -> 用接口+实现类
interface ConfigLoader {
    Config load();
}
​
class FileConfigLoader implements ConfigLoader {
    @Override
    public Config load() {
        return loadFromFile();
    }
}
​
class DatabaseConfigLoader implements ConfigLoader {
    @Override
    public Config load() {
        return loadFromDatabase();
    }
}

二、接口:从“行为契约”到“系统架构”

1. 接口的演进:从“脆弱”到“强大”

用户资料提到了JDK8+的接口新特性,但没解释这些特性如何改变我们的设计:

java

// JDK8之前:接口是“脆弱的”
public interface PaymentService {
    // 只能有抽象方法
    PaymentResult pay(PaymentRequest request);
    RefundResult refund(RefundRequest request);
    
    // 问题:如果要在所有实现中添加新方法,会破坏所有现有实现
    // boolean cancel(String paymentId);  // 添加这个方法会破坏所有实现类
}
​
// JDK8之后:接口变得“强大且兼容”
public interface PaymentService {
    // 抽象方法
    PaymentResult pay(PaymentRequest request);
    
    // 默认方法:提供默认实现,不破坏现有实现
    default RefundResult refund(RefundRequest request) {
        throw new UnsupportedOperationException("退款功能未实现");
    }
    
    // 另一个默认方法
    default boolean cancel(String paymentId) {
        // 默认实现:记录日志,返回false
        log.warn("取消支付功能未实现,paymentId: {}", paymentId);
        return false;
    }
    
    // 静态方法:工具方法
    static PaymentRequestBuilder builder() {
        return new PaymentRequestBuilder();
    }
    
    // 私有方法(JDK9+):辅助方法,不暴露给实现类
    private void validateAmount(BigDecimal amount) {
        if (amount == null || amount.signum() <= 0) {
            throw new IllegalArgumentException("金额必须大于0");
        }
    }
}
​
// 实现类可以只实现需要的方法
class SimplePaymentService implements PaymentService {
    @Override
    public PaymentResult pay(PaymentRequest request) {
        // 只实现支付
        return processPayment(request);
    }
    // refund和cancel使用默认实现
}
​
class FullPaymentService implements PaymentService {
    @Override
    public PaymentResult pay(PaymentRequest request) { /* 实现 */ }
    
    @Override
    public RefundResult refund(RefundRequest request) {
        // 覆盖默认实现
        return processRefund(request);
    }
    
    @Override
    public boolean cancel(String paymentId) {
        // 覆盖默认实现
        return doCancel(paymentId);
    }
}

2. 接口的多重实现与“菱形继承”问题

这是Java面试的经典问题,在实际项目中也会遇到:

java

interface A {
    default void doSomething() {
        System.out.println("A.doSomething");
    }
}
​
interface B {
    default void doSomething() {
        System.out.println("B.doSomething");
    }
}
​
// ❌ 编译错误:类C继承了两个接口的doSomething()默认方法
// class C implements A, B { }
​
// ✅ 解决方案1:明确指定使用哪个
class D implements A, B {
    @Override
    public void doSomething() {
        // 明确调用A的默认方法
        A.super.doSomething();
        // 或者提供自己的实现
    }
}
​
// ✅ 解决方案2:重新抽象
abstract class AbstractA implements A {
    // 把冲突的方法变成抽象,让子类决定
    @Override
    public abstract void doSomething();
}
​
class E extends AbstractA implements B {
    @Override
    public void doSomething() {
        // 现在只需要处理B的默认方法
        B.super.doSomething();
    }
}

在我的支付系统中,我遇到了类似问题:

java

// 定义各种能力接口
interface Payable {
    default PaymentResult pay(PaymentRequest request) {
        return processPayment(request);
    }
    
    PaymentResult processPayment(PaymentRequest request);
}
​
interface Refundable {
    default RefundResult refund(RefundRequest request) {
        return processRefund(request);
    }
    
    RefundResult processRefund(RefundRequest request);
}
​
interface Cancelable {
    default boolean cancel(String paymentId) {
        return doCancel(paymentId);
    }
    
    boolean doCancel(String paymentId);
}
​
// 组合接口:微信支付支持所有功能
interface WechatPaymentService extends Payable, Refundable, Cancelable {
    // 微信特有方法
    String getPaymentQRCode(PaymentRequest request);
}
​
// 具体实现
public class WechatPaymentServiceImpl implements WechatPaymentService {
    // 必须实现所有抽象方法
    @Override
    public PaymentResult processPayment(PaymentRequest request) {
        // 微信支付实现
    }
    
    @Override
    public RefundResult processRefund(RefundRequest request) {
        // 微信退款实现
    }
    
    @Override
    public boolean doCancel(String paymentId) {
        // 微信取消实现
    }
    
    @Override
    public String getPaymentQRCode(PaymentRequest request) {
        // 微信特有
    }
}

3. 接口在Spring框架中的魔法

现代Java开发离不开Spring框架,而接口在Spring中扮演着核心角色:

java

// 业务服务接口
public interface OrderService {
    Order createOrder(OrderRequest request);
    Order getOrder(String orderId);
    Order updateOrder(OrderUpdateRequest request);
    void cancelOrder(String orderId);
    
    // 默认方法:业务规则
    default void validateOrderRequest(OrderRequest request) {
        if (request.getItems() == null || request.getItems().isEmpty()) {
            throw new ValidationException("订单项不能为空");
        }
        if (request.getTotalAmount() == null || request.getTotalAmount().signum() <= 0) {
            throw new ValidationException("订单金额无效");
        }
    }
}
​
// 实现类:Spring管理的Bean
@Service
@Transactional
@Slf4j
public class OrderServiceImpl implements OrderService {
    private final OrderRepository orderRepository;
    private final PaymentService paymentService;
    private final InventoryService inventoryService;
    
    @Autowired
    public OrderServiceImpl(OrderRepository orderRepository,
                           PaymentService paymentService,
                           InventoryService inventoryService) {
        this.orderRepository = orderRepository;
        this.paymentService = paymentService;
        this.inventoryService = inventoryService;
    }
    
    @Override
    public Order createOrder(OrderRequest request) {
        // 1. 使用接口默认方法验证
        validateOrderRequest(request);
        
        // 2. 检查库存
        inventoryService.reserveItems(request.getItems());
        
        // 3. 创建订单
        Order order = Order.fromRequest(request);
        order = orderRepository.save(order);
        
        // 4. 调用支付
        PaymentRequest paymentRequest = buildPaymentRequest(order);
        PaymentResult paymentResult = paymentService.pay(paymentRequest);
        
        // 5. 更新订单状态
        order.updatePaymentStatus(paymentResult);
        return orderRepository.save(order);
    }
    
    // 其他方法实现...
}
​
// 测试时可以使用Mock
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
    @Mock
    private OrderRepository orderRepository;
    
    @Mock
    private PaymentService paymentService;
    
    @InjectMocks
    private OrderServiceImpl orderService;
    
    @Test
    void createOrder_ValidRequest_ReturnsOrder() {
        // 测试接口实现
        OrderRequest request = createValidOrderRequest();
        Order result = orderService.createOrder(request);
        assertNotNull(result);
        verify(paymentService).pay(any());
    }
}

三、抽象类 vs 接口:从“选择困难”到“设计直觉”

经过多次重构,我总结出了一套实用的选择指南:

image-20260201213018587

1. 决策流程图

text

需要建模什么?
├── 是“is-a”关系(狗是一种动物)?
│   ├── 有共享的状态/实现吗?
│   │   ├── 是 → 抽象类
│   │   └── 否 → 接口
│   └── 需要多重继承吗?
│       ├── 是 → 接口
│       └── 否 → 继续判断
└── 是“has-a”能力(狗会游泳)?
    └── 接口

2. 实际案例分析

案例1:日志框架设计

java

// 抽象类:有共享状态和模板方法
abstract class AbstractLogger {
    protected LogLevel level;
    protected Appender appender;
    
    // 模板方法
    public final void log(LogLevel level, String message) {
        if (isEnabled(level)) {
            String formatted = format(message);
            doLog(formatted);
        }
    }
    
    protected abstract String format(String message);
    protected abstract void doLog(String formattedMessage);
    
    protected boolean isEnabled(LogLevel level) {
        return this.level.ordinal() >= level.ordinal();
    }
}

// 接口:定义日志能力
interface Auditable {
    void audit(String action, String userId, Object details);
}

// 类可以同时继承抽象类和实现接口
class FileLogger extends AbstractLogger implements Auditable {
    private final File logFile;
    
    @Override
    protected String format(String message) {
        return Instant.now() + " [" + level + "] " + message;
    }
    
    @Override
    protected void doLog(String formattedMessage) {
        writeToFile(logFile, formattedMessage);
    }
    
    @Override
    public void audit(String action, String userId, Object details) {
        log(LogLevel.INFO, "AUDIT: " + action + " by " + userId);
        saveAuditRecord(action, userId, details);
    }
}

案例2:电商促销系统

java

// 接口:定义促销策略
interface PromotionStrategy {
    BigDecimal apply(BigDecimal originalPrice, OrderContext context);
    
    // 默认方法:验证策略是否适用
    default boolean isApplicable(OrderContext context) {
        return context.getCustomerType() == CustomerType.REGULAR;
    }
    
    // 静态方法:工厂方法
    static PromotionStrategy of(String type) {
        switch (type) {
            case "DISCOUNT": return new DiscountStrategy();
            case "COUPON": return new CouponStrategy();
            case "FLASH_SALE": return new FlashSaleStrategy();
            default: throw new IllegalArgumentException("未知促销类型");
        }
    }
}

// 抽象类:共享促销计算逻辑
abstract class AbstractPromotionStrategy implements PromotionStrategy {
    protected final PromotionRule rule;
    
    protected AbstractPromotionStrategy(PromotionRule rule) {
        this.rule = rule;
    }
    
    @Override
    public BigDecimal apply(BigDecimal originalPrice, OrderContext context) {
        if (!isApplicable(context)) {
            return originalPrice;  // 不适用,返回原价
        }
        
        BigDecimal discounted = calculateDiscount(originalPrice, context);
        return discounted.max(BigDecimal.ZERO);  // 确保价格不为负
    }
    
    protected abstract BigDecimal calculateDiscount(BigDecimal originalPrice, OrderContext context);
    
    // 公共辅助方法
    protected BigDecimal getDiscountRate(OrderContext context) {
        if (context.isVIP()) {
            return rule.getVipDiscountRate();
        }
        return rule.getRegularDiscountRate();
    }
}

// 具体策略
class DiscountStrategy extends AbstractPromotionStrategy {
    public DiscountStrategy() {
        super(PromotionRule.DEFAULT_DISCOUNT);
    }
    
    @Override
    protected BigDecimal calculateDiscount(BigDecimal originalPrice, OrderContext context) {
        BigDecimal rate = getDiscountRate(context);
        return originalPrice.multiply(BigDecimal.ONE.subtract(rate));
    }
}

四、代码块:从“语法技巧”到“工程实践”

1. 静态代码块的真实应用

image-20260201213556805

用户资料只展示了基础用法,实际项目中静态代码块有更重要的作用:

java

// 场景1:加载配置文件
public class AppConfig {
    private static final Properties config = new Properties();
    
    static {
        // 类加载时执行,确保配置提前加载
        try (InputStream is = AppConfig.class.getClassLoader()
                .getResourceAsStream("application.properties")) {
            if (is != null) {
                config.load(is);
            } else {
                // 加载默认配置
                loadDefaultConfig();
            }
        } catch (IOException e) {
            // 静态代码块不能抛出受检异常,必须处理
            throw new RuntimeException("加载配置文件失败", e);
        }
    }
    
    private static void loadDefaultConfig() {
        config.setProperty("server.port", "8080");
        config.setProperty("database.url", "jdbc:mysql://localhost:3306/app");
        // ...
    }
    
    public static String getProperty(String key) {
        return config.getProperty(key);
    }
}

// 场景2:注册服务(SPI机制)
public class ServiceRegistry {
    private static final Map<String, ServiceProvider> providers = new HashMap<>();
    
    static {
        // 自动发现并注册所有ServiceProvider实现
        ServiceLoader<ServiceProvider> loader = ServiceLoader.load(ServiceProvider.class);
        for (ServiceProvider provider : loader) {
            providers.put(provider.getServiceName(), provider);
            log.info("注册服务提供者: {}", provider.getServiceName());
        }
        
        if (providers.isEmpty()) {
            log.warn("未发现任何服务提供者");
        }
    }
    
    public static ServiceProvider getProvider(String serviceName) {
        return providers.get(serviceName);
    }
}

// 场景3:初始化复杂静态数据结构
public class ColorPalette {
    // 静态常量映射
    private static final Map<String, Color> NAMED_COLORS;
    
    static {
        // 复杂的初始化逻辑
        Map<String, Color> map = new HashMap<>();
        
        // 基础颜色
        map.put("RED", new Color(255, 0, 0));
        map.put("GREEN", new Color(0, 255, 0));
        map.put("BLUE", new Color(0, 0, 255));
        
        // 从文件加载自定义颜色
        try {
            List<ColorDefinition> definitions = loadColorDefinitions();
            for (ColorDefinition def : definitions) {
                map.put(def.getName(), def.toColor());
            }
        } catch (IOException e) {
            log.warn("加载自定义颜色失败,使用默认颜色", e);
        }
        
        // 使映射不可变
        NAMED_COLORS = Collections.unmodifiableMap(map);
    }
    
    public static Color getColor(String name) {
        return NAMED_COLORS.get(name.toUpperCase());
    }
}

2. 实例代码块的妙用:减少构造器重复代码

java

// 反模式:多个构造器有重复代码
public class ConnectionPool {
    private final DataSource dataSource;
    private final int minSize;
    private final int maxSize;
    private final Duration timeout;
    
    public ConnectionPool(DataSource dataSource) {
        this.dataSource = dataSource;
        this.minSize = 10;  // 重复代码
        this.maxSize = 100; // 重复代码
        this.timeout = Duration.ofSeconds(30);  // 重复代码
        validate();  // 重复代码
    }
    
    public ConnectionPool(DataSource dataSource, int minSize, int maxSize) {
        this.dataSource = dataSource;
        this.minSize = minSize;
        this.maxSize = maxSize;
        this.timeout = Duration.ofSeconds(30);  // 重复代码
        validate();  // 重复代码
    }
    
    // 更多构造器...
}

// 正解:使用实例代码块
public class ConnectionPool {
    private final DataSource dataSource;
    private final int minSize;
    private final int maxSize;
    private final Duration timeout;
    
    // 实例代码块:所有构造器共享的初始化逻辑
    {
        // 公共验证逻辑
        validateDataSource();
        
        // 设置默认值(可在构造器中覆盖)
        this.timeout = Duration.ofSeconds(30);
        
        // 初始化资源
        initializePool();
    }
    
    public ConnectionPool(DataSource dataSource) {
        this.dataSource = Objects.requireNonNull(dataSource, "数据源不能为空");
        this.minSize = 10;
        this.maxSize = 100;
    }
    
    public ConnectionPool(DataSource dataSource, int minSize, int maxSize) {
        this.dataSource = Objects.requireNonNull(dataSource, "数据源不能为空");
        this.minSize = minSize;
        this.maxSize = maxSize;
    }
    
    private void validateDataSource() {
        // 验证数据源
    }
    
    private void initializePool() {
        // 初始化连接池
    }
}

五、内部类:从“嵌套语法”到“设计模式”

image-20260201213852291

1. 成员内部类的真实应用:迭代器模式

java

// 自定义集合类的迭代器实现
public class FixedSizeList<E> implements Iterable<E> {
    private final E[] elements;
    private int size;
    
    public FixedSizeList(int capacity) {
        this.elements = (E[]) new Object[capacity];
    }
    
    public void add(E element) {
        if (size >= elements.length) {
            throw new IllegalStateException("列表已满");
        }
        elements[size++] = element;
    }
    
    // 成员内部类:迭代器
    @Override
    public Iterator<E> iterator() {
        return new FixedSizeListIterator();
    }
    
    // 私有内部类:可以直接访问外部类的私有字段
    private class FixedSizeListIterator implements Iterator<E> {
        private int cursor = 0;
        
        @Override
        public boolean hasNext() {
            return cursor < size;  // 访问外部类的size字段
        }
        
        @Override
        public E next() {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            return elements[cursor++];  // 访问外部类的elements数组
        }
        
        @Override
        public void remove() {
            throw new UnsupportedOperationException("不支持删除操作");
        }
    }
    
    // 静态内部类:构建器
    public static class Builder<E> {
        private int capacity = 10;
        
        public Builder<E> capacity(int capacity) {
            if (capacity <= 0) {
                throw new IllegalArgumentException("容量必须大于0");
            }
            this.capacity = capacity;
            return this;
        }
        
        public FixedSizeList<E> build() {
            return new FixedSizeList<>(capacity);
        }
    }
}

// 使用
FixedSizeList<String> list = new FixedSizeList.Builder<String>()
    .capacity(20)
    .build();

for (String item : list) {  // 使用内部类实现的迭代器
    System.out.println(item);
}

2. 匿名内部类在现代Java中的演变

java

// Java 8之前:匿名内部类常用作事件监听器
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("按钮被点击");
        doSomething();
    }
    
    private void doSomething() {
        // 额外逻辑
    }
});

// Java 8之后:Lambda表达式替代简单的匿名内部类
button.addActionListener(e -> {
    System.out.println("按钮被点击");
    doSomething();  // 不能定义额外方法
});

// 但是,复杂的匿名内部类仍然有用武之地
// 场景:创建一次性实现的策略
public class PaymentProcessor {
    private RetryStrategy retryStrategy;
    
    public void setRetryStrategy(RetryStrategy strategy) {
        this.retryStrategy = strategy;
    }
    
    public void processWithRetry(PaymentRequest request) {
        // 使用匿名内部类定义自定义重试策略
        RetryStrategy customStrategy = new RetryStrategy() {
            private int attemptCount = 0;
            
            @Override
            public boolean shouldRetry(Exception e, int attempt) {
                attemptCount = attempt;
                
                if (e instanceof TimeoutException) {
                    // 超时重试3次
                    return attempt < 3;
                }
                
                if (e instanceof NetworkException) {
                    // 网络错误重试5次,但每次延迟增加
                    return attempt < 5;
                }
                
                // 其他错误不重试
                return false;
            }
            
            @Override
            public Duration getDelay(int attempt) {
                // 指数退避
                return Duration.ofSeconds((long) Math.pow(2, attempt));
            }
            
            @Override
            public void onRetry(int attempt, Exception e) {
                log.warn("第{}次重试,错误: {}", attempt, e.getMessage());
            }
        };
        
        setRetryStrategy(customStrategy);
        process(request);
    }
}

3. 静态内部类的最佳实践:建造者模式

java

// 不可变类的建造者模式
public final class HttpRequest {
    private final String method;
    private final String url;
    private final Map<String, String> headers;
    private final String body;
    
    // 私有构造器
    private HttpRequest(Builder builder) {
        this.method = builder.method;
        this.url = builder.url;
        this.headers = Collections.unmodifiableMap(new HashMap<>(builder.headers));
        this.body = builder.body;
    }
    
    // 静态内部类:建造者
    public static class Builder {
        private String method = "GET";
        private String url;
        private Map<String, String> headers = new HashMap<>();
        private String body;
        
        public Builder url(String url) {
            this.url = Objects.requireNonNull(url, "URL不能为空");
            return this;
        }
        
        public Builder method(String method) {
            this.method = Objects.requireNonNull(method, "方法不能为空").toUpperCase();
            if (!Arrays.asList("GET", "POST", "PUT", "DELETE").contains(this.method)) {
                throw new IllegalArgumentException("不支持的HTTP方法: " + method);
            }
            return this;
        }
        
        public Builder header(String name, String value) {
            this.headers.put(
                Objects.requireNonNull(name, "头部名称不能为空"),
                Objects.requireNonNull(value, "头部值不能为空")
            );
            return this;
        }
        
        public Builder body(String body) {
            this.body = body;
            return this;
        }
        
        public HttpRequest build() {
            if (url == null || url.trim().isEmpty()) {
                throw new IllegalStateException("URL必须设置");
            }
            return new HttpRequest(this);
        }
    }
    
    // getter方法...
}

// 使用
HttpRequest request = new HttpRequest.Builder()
    .url("https://api.example.com/users")
    .method("POST")
    .header("Content-Type", "application/json")
    .header("Authorization", "Bearer token123")
    .body("{\"name\":\"张三\"}")
    .build();

六、综合实战:设计一个可扩展的通知系统

让我用今天的所有知识,设计一个生产级的通知系统:

image-20260201214212821

java

// 1. 接口定义通知行为
public interface Notification {
    String getId();
    String getTitle();
    String getContent();
    NotificationType getType();
    Instant getCreatedAt();
    
    // 默认方法:验证通知
    default boolean isValid() {
        return getTitle() != null && !getTitle().trim().isEmpty()
            && getContent() != null && !getContent().trim().isEmpty()
            && getType() != null;
    }
    
    // 静态工厂方法
    static Notification create(String title, String content, NotificationType type) {
        return new SimpleNotification(title, content, type);
    }
}
​
// 2. 抽象类定义通知模板
public abstract class AbstractNotification implements Notification {
    protected final String id;
    protected final String title;
    protected final String content;
    protected final NotificationType type;
    protected final Instant createdAt;
    
    protected AbstractNotification(String title, String content, NotificationType type) {
        this.id = UUID.randomUUID().toString();
        this.title = Objects.requireNonNull(title, "标题不能为空");
        this.content = Objects.requireNonNull(content, "内容不能为空");
        this.type = Objects.requireNonNull(type, "类型不能为空");
        this.createdAt = Instant.now();
    }
    
    @Override
    public String getId() { return id; }
    @Override
    public String getTitle() { return title; }
    @Override
    public String getContent() { return content; }
    @Override
    public NotificationType getType() { return type; }
    @Override
    public Instant getCreatedAt() { return createdAt; }
    
    // 抽象方法:子类定义具体格式
    public abstract String getFormattedContent();
    
    // 模板方法:发送通知的固定流程
    public final void send() {
        if (!isValid()) {
            throw new IllegalStateException("通知无效");
        }
        
        preSend();
        doSend();
        postSend();
    }
    
    protected void preSend() {
        log.info("准备发送通知: {}", id);
    }
    
    protected abstract void doSend();
    
    protected void postSend() {
        log.info("通知发送完成: {}", id);
        recordDelivery();
    }
    
    private void recordDelivery() {
        // 记录投递状态
    }
}
​
// 3. 具体通知类
public class EmailNotification extends AbstractNotification {
    private final String recipientEmail;
    private final String subject;
    
    // 静态内部类:建造者
    public static class Builder {
        private String title;
        private String content;
        private String recipientEmail;
        private String subject;
        
        public Builder title(String title) {
            this.title = title;
            return this;
        }
        
        public Builder content(String content) {
            this.content = content;
            return this;
        }
        
        public Builder recipientEmail(String email) {
            this.recipientEmail = email;
            return this;
        }
        
        public Builder subject(String subject) {
            this.subject = subject;
            return this;
        }
        
        public EmailNotification build() {
            return new EmailNotification(this);
        }
    }
    
    private EmailNotification(Builder builder) {
        super(builder.title, builder.content, NotificationType.EMAIL);
        this.recipientEmail = Objects.requireNonNull(builder.recipientEmail, "收件人邮箱不能为空");
        this.subject = builder.subject != null ? builder.subject : builder.title;
    }
    
    @Override
    public String getFormattedContent() {
        return String.format("""
            主题: %s
            收件人: %s
            内容: %s
            """, subject, recipientEmail, getContent());
    }
    
    @Override
    protected void doSend() {
        // 发送邮件逻辑
        EmailSender.send(recipientEmail, subject, getContent());
    }
    
    // 实例代码块:注册到通知管理器
    {
        NotificationManager.register(this);
    }
}
​
// 4. 通知管理器(使用静态代码块初始化)
public class NotificationManager {
    private static final Map<NotificationType, List<NotificationHandler>> handlers = new HashMap<>();
    
    // 静态代码块:初始化处理器映射
    static {
        // 加载所有处理器
        ServiceLoader<NotificationHandler> loader = ServiceLoader.load(NotificationHandler.class);
        for (NotificationHandler handler : loader) {
            handlers.computeIfAbsent(handler.getSupportedType(), k -> new ArrayList<>())
                   .add(handler);
        }
        
        // 注册默认处理器
        registerDefaultHandlers();
    }
    
    private static void registerDefaultHandlers() {
        registerHandler(NotificationType.EMAIL, new EmailNotificationHandler());
        registerHandler(NotificationType.SMS, new SmsNotificationHandler());
        registerHandler(NotificationType.PUSH, new PushNotificationHandler());
    }
    
    public static void registerHandler(NotificationType type, NotificationHandler handler) {
        handlers.computeIfAbsent(type, k -> new ArrayList<>()).add(handler);
    }
    
    public static void register(Notification notification) {
        List<NotificationHandler> typeHandlers = handlers.get(notification.getType());
        if (typeHandlers != null) {
            for (NotificationHandler handler : typeHandlers) {
                handler.handle(notification);
            }
        }
    }
    
    // 成员内部类:处理器接口
    public interface NotificationHandler {
        NotificationType getSupportedType();
        void handle(Notification notification);
    }
    
    // 静态内部类:配置
    public static class Config {
        private int maxRetries = 3;
        private Duration timeout = Duration.ofSeconds(30);
        
        public Config maxRetries(int maxRetries) {
            this.maxRetries = maxRetries;
            return this;
        }
        
        public Config timeout(Duration timeout) {
            this.timeout = timeout;
            return this;
        }
        
        public void apply() {
            // 应用配置
        }
    }
}
​
// 5. 枚举定义通知类型
public enum NotificationType {
    EMAIL("邮件"),
    SMS("短信"),
    PUSH("推送"),
    WECHAT("微信"),
    DINGTALK("钉钉");
    
    private final String description;
    
    // 枚举构造器
    NotificationType(String description) {
        this.description = description;
    }
    
    public String getDescription() {
        return description;
    }
    
    // 静态方法:从字符串解析
    public static NotificationType fromString(String value) {
        for (NotificationType type : values()) {
            if (type.name().equalsIgnoreCase(value)) {
                return type;
            }
        }
        throw new IllegalArgumentException("未知的通知类型: " + value);
    }
}

七、设计原则与最佳实践总结

1. 抽象类使用原则
  • 模板方法模式:当有固定算法骨架,只有某些步骤变化时

  • 代码复用:多个类有共享的非公共代码时(接口做不到)

  • 控制构造:需要控制子类创建过程时

  • 避免:如果只有静态方法,使用工具类而非抽象类

2. 接口设计原则
  • 定义能力:has-a关系,类能做什么

  • 解耦:客户端代码依赖接口,而非具体实现

  • 默认方法:用于API演进,添加新方法不破坏现有实现

  • 静态方法:接口相关的工具方法

  • 多重继承:类需要多种能力时

3. 代码块使用指南
  • 静态代码块:复杂的静态初始化、加载资源、注册服务

  • 实例代码块:多个构造器的公共初始化逻辑

  • 注意:静态代码块不能抛出受检异常,必须处理

4. 内部类选择矩阵

text

需要什么特性?
├── 需要访问外部实例所有成员 → 成员内部类
├── 需要独立,不依赖外部实例 → 静态内部类
├── 只在方法内使用 → 局部内部类
└── 一次性实现,不需要名字 → 匿名内部类

结语:从语法理解到设计直觉

三年前那个让我重构的支付系统,今天看来是我面向对象设计思维的成人礼。它让我明白:

抽象类不是"有抽象方法的类",而是"部分实现的模板"——它封装了不变的部分,开放了变化的部分。

接口不是"只有方法声明的类",而是"能力的契约"——它定义了系统各部分的协作协议。

代码块不是"语法技巧",而是"生命周期管理工具"——它们在对象的诞生和类的加载中扮演关键角色。

内部类不是"嵌套的语法糖",而是"封装和组织的艺术"——它们让紧密相关的逻辑呆在一起。

Day07的内容,是Java面向对象设计从"会用"到"精通"的关键一跃。今天你理解的这些概念,将决定你未来是写"正确的代码",还是设计"优雅的系统"。

记住:技术会过时,语法会变化,但设计思想永存。好的设计,是经得起时间考验的。

明天,当你学习函数式编程和Lambda表达式时,你会看到这些面向对象概念如何与函数式思想融合。但那是明天的故事了。

今天,好好思考你设计的每一个抽象、每一个接口——它们不只是代码,更是你对问题本质的洞察和表达。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐