静态工厂方法是什么
用有名字的静态方法代替构造器创建对象:方法名可以表达意图(of、valueOf、parse、format、create),返回值可以是子类型,也可以复用缓存实例。它是工厂方法模式在语言层面的轻量变体,Effective Java 把「考虑用静态工厂方法代替构造器」列为第一条建议。缺点是不易被继承扩展、无法通过反射按约定实例化。
基本信息
| 命名惯例 | of / valueOf / parse / format / create / newInstance |
|---|---|
| 收益 | 意图显式、实例可控、可返回子类型 |
| 对比 | 构造函数重载靠参数类型区分,可读性差 |
| 代价 | 类难以被继承;框架按构造器约定实例化时不适用 |
判断与用法
- 多个重载构造器参数类型相同、语义不同时改用命名方法
- 需要返回接口或抽象类的实现时,用静态工厂隐藏实现类
- 实例可被复用(常量、值对象、枚举式有限集合)时,用工厂控制实例数量
- 需要表达解析语义(parse)或格式化语义(format)时,命名比构造器清楚
- 需要在创建时校验并给出明确异常信息时,把校验写在工厂里
- 需要子类化定制时转向构造器或保护型工厂,而不是硬套静态方法
Java 示例
Java 示例
Money usd = Money.of("USD", 10); // 语义清晰的创建入口
Instant t = Instant.parse("2026-10-12T00:00:00Z"); // 解析用 parse
List<String> empty = List.of(); // 复用不可变空实例
boolean b = Boolean.valueOf(true); // 可缓存实例
// 缓存实例必须返回不可变对象,否则享元被调用方改坏
private static final Money ZERO = Money.of("CNY", 0);
public static Money zero() { return ZERO; }命名惯例含义
| 方法名 | 含义 | 示例 |
|---|---|---|
| from / of | 类型转换,参数是来源对象 | Date.from(instant) |
| valueOf | 与 of 同义的更啰嗦写法 | BigInteger.valueOf(n) |
| parse / format | 字符串解析与格式化 | LocalDate.parse(text) |
| create / newInstance | 每次返回新实例 | createBuffer() |
| getInstance | 返回实例,可能缓存或单例 | getInstance(type) |
静态工厂的常见误用是行为不一致:同类里有的工厂会校验参数,有的不校验,调用方无法预期。约定一种风格并保持,比方法名本身更重要。
缓存实例时返回不可变对象:可变实例被缓存后,任何调用方修改都会影响所有拿到同一实例的地方,这类缺陷很难定位。