襄阳市网站建设_网站建设公司_会员系统_seo优化
2025/12/24 19:21:27 网站建设 项目流程

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

一、学习目标

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

二、核心知识点详解

1. 《Effective Java》核心原则

✅ 避免创建不必要的对象

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

示例

// 不推荐:每次调用都创建新对象
String s = new String("hello");
// 推荐:使用字符串字面量(JVM 自动复用)
String s = "hello";

最佳实践

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

优势

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

示例

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)

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

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

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

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

特点:首次调用时创建实例,节省资源,但多线程下不安全。
仅适用于单线程环境(不推荐生产使用)。

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 防止指令重排序。

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;
}
}

单元测试验证线程安全

@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 类加载机制实现延迟加载 + 线程安全,无锁、简洁、高效。
推荐作为默认实现。

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)

特点:最简洁、最安全的单例实现。
天然防止反射和序列化攻击。

public enum SingletonEnum {
INSTANCE;
public void doSomething() {
// 业务逻辑
}
}

安全性

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

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

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

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

▶ 如何防御序列化攻击?

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

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

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

private SingletonDCL() {
if (instance != null) {
throw new RuntimeException("Singleton already initialized!");
}
}

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

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

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

结构

  • 抽象产品(Product)
  • 具体产品(ConcreteProductA/B)
  • 工厂(Creator)
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. 策略模式 + 工厂模式组合(高阶实践)

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

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

@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 表达式简化:

// 无需定义类
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 循环。
// 传统写法
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 高质量编码系列。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询