代码优化与设计模式 — 实战精要

作者 :小股虫
标签 :Java、设计模式、代码重构、Effective Java、性能优化
适用人群:Java 中高级开发者、准备面试者、追求代码质量的工程师

一、学习目标

  • 掌握《Effective Java》中关于对象创建与静态工厂方法的核心原则
  • 理解并熟练应用关键设计模式:单例模式(5种实现)、工厂方法、策略模式
  • 运用代码重构技巧提升项目可维护性与性能
  • 完成三项实践任务:优化单例实现、重构重复逻辑、撰写技术博客

二、核心知识点详解

1. 《Effective Java》核心原则

✅ 避免创建不必要的对象

问题:频繁创建相同功能的对象会增加 GC 压力,降低性能。

示例

java 复制代码
// 不推荐:每次调用都创建新对象
String s = new String("hello");

// 推荐:使用字符串字面量(JVM 自动复用)
String s = "hello";

最佳实践

  • 对不可变对象(如 String、Integer)尽量复用;
  • 使用缓存(如 ConcurrentHashMap 缓存计算结果);
  • 谨慎使用对象池(除非对象创建成本极高)。
✅ 使用静态工厂方法替代构造器

优势

  • 方法有名字,语义更清晰(如 BigInteger.probablePrime());
  • 可返回子类型对象(如 Collections.unmodifiableList());
  • 可控制实例数量(如单例、享元);
  • 支持泛型类型推断(避免冗长泛型声明)。

示例

java 复制代码
public class BooleanUtils {
    public static Boolean valueOf(boolean b) {
        return b ? Boolean.TRUE : Boolean.FALSE; // 复用常量
    }
}

注意事项

  • 静态工厂方法不能被继承;
  • 需在文档中明确说明是否返回缓存实例、是否线程安全;
  • 不要让用户误以为"轻量"而滥用(内部可能很重)。

📌 参考 :《Effective Java》Item 1 & Item 5

🔗 延伸建议:Item 6(避免 finalize)、Item 17(最小化可变性)、Item 42(优先使用 Lambda)

2. 单例模式(Singleton Pattern)

单例模式确保一个类只有一个实例,并提供全局访问点。根据初始化时机与线程安全性,有多种实现方式。

(1) 饿汉式(Eager Initialization)

特点 :类加载时即创建实例,线程安全,但无延迟加载。
适用场景:实例占用资源小,且一定会被使用。

java 复制代码
public class SingletonEager {
    // 类加载时即初始化
    private static final SingletonEager INSTANCE = new SingletonEager();
    
    private SingletonEager() {}
    
    public static SingletonEager getInstance() {
        return INSTANCE;
    }
}

⚠️ 安全性:线程安全 ✅,但不防反射攻击 ❌,若实现 Serializable 则需 readResolve() 防序列化攻击。

(2) 懒汉式(Lazy Initialization,非线程安全)

特点 :首次调用时创建实例,节省资源,但多线程下不安全。

仅适用于单线程环境(不推荐生产使用)。

java 复制代码
public class SingletonLazy {
    private static SingletonLazy instance;
    
    private SingletonLazy() {}
    
    public static SingletonLazy getInstance() {
        if (instance == null) {
            instance = new SingletonLazy(); // 多线程下可能创建多个实例
        }
        return instance;
    }
}

⚠️ 安全性:线程安全 ❌,防反射 ❌,防序列化 ❌。

(3) 双重校验锁(Double-Checked Locking)

特点 :延迟加载 + 线程安全 + 性能较高。
关键:volatile 防止指令重排序。

java 复制代码
public class SingletonDCL {
    private static volatile SingletonDCL instance;
    
    private SingletonDCL() {}
    
    public static SingletonDCL getInstance() {
        if (instance == null) {
            synchronized (SingletonDCL.class) {
                if (instance == null) {
                    instance = new SingletonDCL();
                }
            }
        }
        return instance;
    }
}

单元测试验证线程安全

java 复制代码
@Test
public void testSingletonThreadSafety() throws InterruptedException {
    Set<SingletonDCL> instances = ConcurrentHashMap.newKeySet();
    ExecutorService executor = Executors.newFixedThreadPool(10);
    
    for (int i = 0; i < 1000; i++) {
        executor.submit(() -> instances.add(SingletonDCL.getInstance()));
    }
    
    executor.shutdown();
    executor.awaitTermination(5, TimeUnit.SECONDS);
    assertEquals(1, instances.size());
}

⚠️ 安全性:线程安全 ✅,但不防反射攻击 ❌,若实现 Serializable 则需 readResolve() 防序列化攻击。

(4) 静态内部类(Holder 模式)

特点 :利用 JVM 类加载机制实现延迟加载 + 线程安全,无锁、简洁、高效。

推荐作为默认实现。

java 复制代码
public class SingletonHolder {
    private SingletonHolder() {}
    
    private static class Holder {
        static final SingletonHolder INSTANCE = new SingletonHolder();
    }
    
    public static SingletonHolder getInstance() {
        return Holder.INSTANCE;
    }
}

安全性

  • 线程安全 ✅(JVM 保证);
  • 若未实现 Serializable,则天然不会被序列化,默认防序列化攻击;
  • 但仍不防反射攻击(可通过构造器绕过);
  • 一旦实现 Serializable,必须添加 readResolve()。
(5) 枚举单例(Enum Singleton)

特点 :最简洁、最安全的单例实现。

天然防止反射和序列化攻击。

java 复制代码
public enum SingletonEnum {
    INSTANCE;
    
    public void doSomething() {
        // 业务逻辑
    }
}

安全性

  • 线程安全 ✅;
  • 延迟加载 ✅(枚举常量在首次使用时初始化);
  • 防反射攻击 ✅(Java 禁止通过反射创建枚举实例);
  • 防序列化攻击 ✅(反序列化时通过 name 查找已有实例,不会新建)。

💡 Joshua Bloch 在《Effective Java》Item 3 中强烈推荐此方式。

🔒 单例安全特性对比总结
实现方式 线程安全 延迟加载 防反射攻击 防序列化攻击 推荐度
饿汉式 ❌(需 readResolve) ★★☆
懒汉式(非线程安全)
双重校验锁 ❌(需 readResolve) ★★★★
静态内部类(Holder) ★★★★★
枚举单例 ★★★★☆

:静态内部类"默认防序列化攻击"的前提是未实现 Serializable。一旦实现,必须手动添加 readResolve() 方法。

▶ 如何防御序列化攻击?

若单例类实现了 Serializable,必须添加:

java 复制代码
private Object readResolve() {
    return getInstance(); // 或直接返回 Holder.INSTANCE / INSTANCE(枚举无需)
}
▶ 如何防御反射攻击?

在构造器中检查实例是否已存在(对非枚举有效):

java 复制代码
private SingletonDCL() {
    if (instance != null) {
        throw new RuntimeException("Singleton already initialized!");
    }
}

⚠️ 此方法对静态内部类无效(因 instance 是内部类字段),唯一完全防反射的是枚举。

3. 工厂方法模式(Factory Method)

目的:将对象创建逻辑封装,解耦客户端与具体类。

结构

  • 抽象产品(Product)
  • 具体产品(ConcreteProductA/B)
  • 工厂(Creator)
java 复制代码
interface PaymentProcessor {
    void process(double amount);
}

class AlipayProcessor implements PaymentProcessor {
    public void process(double amount) { /* 支付宝逻辑 */ }
}

class WechatProcessor implements PaymentProcessor {
    public void process(double amount) { /* 微信逻辑 */ }
}

class PaymentFactory {
    public static PaymentProcessor create(String type) {
        return switch (type.toLowerCase()) {
            case "alipay" -> new AlipayProcessor();
            case "wechat" -> new WechatProcessor();
            default -> throw new IllegalArgumentException("Unknown payment type");
        };
    }
}

4. 策略模式 + 工厂模式组合(高阶实践)

典型场景:动态选择算法(如折扣、税率、支付渠道)。

组合优势:策略定义行为,工厂管理创建,两者结合实现"开闭原则"。

java 复制代码
@FunctionalInterface
interface TaxStrategy {
    BigDecimal calculate(BigDecimal income);
}

class ChinaTax implements TaxStrategy {
    public BigDecimal calculate(BigDecimal income) {
        return income.multiply(BigDecimal.valueOf(0.1));
    }
}

class USTax implements TaxStrategy {
    public BigDecimal calculate(BigDecimal income) {
        return income.multiply(BigDecimal.valueOf(0.15));
    }
}

// 策略工厂
class TaxStrategyFactory {
    private static final Map<String, TaxStrategy> STRATEGIES = Map.of(
        "CN", new ChinaTax(),
        "US", new USTax()
    );
    
    public static TaxStrategy get(String country) {
        return STRATEGIES.get(country);
    }
}

🌟 进阶 :在 Spring 中可通过 Map<String, TaxStrategy> 自动注入所有策略 Bean,实现插件化扩展。

5. 策略模式与函数式编程

Java 8+ 后,策略可用 Lambda 表达式简化:

java 复制代码
// 无需定义类
TaxStrategy cnTax = income -> income.multiply(BigDecimal.valueOf(0.1));

// 或直接内联
orderService.setDiscountStrategy(price -> price * 0.75);

函数式带来的额外价值

  • 组合性:Predicate.and(), Function.andThen()
  • 惰性求值:Stream 中间操作不立即执行
  • 无状态:纯函数更易测试和并行

6. 代码重构技巧

(1) 消除重复代码(DRY 原则)
  • 提取公共方法;
  • 使用模板方法或策略模式抽象差异点;
  • 利用工具类封装通用逻辑。
(2) 优化循环
  • 避免在循环内重复计算;
  • 减少嵌套层级(提前 return 或 continue);
  • 考虑使用 Stream 替代传统 for 循环。
java 复制代码
// 传统写法
for (var item : list) {
    if (item != null && item.isActive()) {
        // ...
    }
}

// 函数式写法
list.stream()
    .filter(Objects::nonNull)
    .filter(Item::isActive)
    .forEach(this::processItem);
(3) 使用函数式编程提升可读性与扩展性
  • Function<T, R>Predicate<T>Consumer<T> 替代匿名类;
  • 链式调用表达业务流程;
  • 支持并行流(.parallelStream())轻松实现多线程处理。

三、总结

  • 单例模式
    • 日常开发推荐 静态内部类
    • 安全敏感系统(如金融、认证)推荐 枚举单例
    • 避免使用非线程安全的懒汉式。
  • 工厂 + 策略:是消除条件分支、提升扩展性的黄金组合;
  • 静态工厂方法:不仅是语法糖,更是设计灵活性的体现;
  • 重构必须有测试护航:行为不变是底线,性能提升是加分项。

代码不是写出来的,是重构出来的。

每一次小优化,都是向"优雅"靠近的一步。

四、参考文献

  1. Joshua Bloch. 《Effective Java》(第3版)
  2. Martin Fowler. 《重构:改善既有代码的设计》
  3. GoF. 《设计模式:可复用面向对象软件的基础》
  4. Oracle Java Tutorials: Lambda Expressions, Concurrency, Serialization

欢迎点赞、收藏、评论交流!

关注我,持续更新 Java 高质量编码系列。

相关推荐
倚肆32 分钟前
MyBatis XML 配置详解
xml·java·mybatis
f***241132 分钟前
SpringBoot中整合ONLYOFFICE在线编辑
java·spring boot·后端
j***121535 分钟前
Java进阶(ElasticSearch的安装与使用)
java·elasticsearch·jenkins
kesifan37 分钟前
JAVA异常处理的基本概念
java·开发语言
K***658940 分钟前
Tomcat下载,安装,配置终极版(2024)
java·tomcat
x***381642 分钟前
springboot整合最新版minio和minio的安装(完整教程,新人必看)
java·spring boot·后端
通往曙光的路上1 小时前
陪玩小项目努力
java
w***4811 小时前
Maven Spring框架依赖包
java·spring·maven
汤姆yu1 小时前
基于springboot的乡村信息建设管理系统
java·spring boot·后端