《Effective Java》:Java不可变值类与静态工厂方法的关联解析
首先得明确:这里说的「不存在两个相等的实例」,本质是实例受控(controlled instantiation)——同一个值永远对应同一个对象实例,不会有重复。这就意味着对于不可变值类,a.equals(b)成立的同时,a == b也必然成立,反过来也一样。
为什么构造方法做不到?
构造方法公开的情况下,外部可以通过new关键字随意创建实例,哪怕值完全相同,每次new都会生成一个全新的对象。比如下面这个简单的不可变颜色类:
// 用构造方法的不可变颜色类,无法保证实例唯一 public final class Color { private final int rgb; // 公开构造方法,外部可随意new public Color(int rgb) { this.rgb = rgb; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Color color = (Color) o; return rgb == color.rgb; } @Override public int hashCode() { return Integer.hashCode(rgb); } public int getRgb() { return rgb; } } // 测试 public class Test { public static void main(String[] args) { Color red1 = new Color(0xFF0000); Color red2 = new Color(0xFF0000); System.out.println(red1.equals(red2)); // true,值相等 System.out.println(red1 == red2); // false,不是同一个实例 } }
这里两个红色实例值相等,但不是同一个对象,所以==返回false,不符合「不存在两个相等实例」的要求。
静态工厂方法怎么实现?
核心思路是:把构造方法设为私有,通过静态工厂方法控制实例的创建与复用。通常会维护一个缓存(比如Map),存储已创建的实例,每次请求先检查缓存,有就直接返回,没有再创建并存入缓存。
修改上面的Color类,用静态工厂实现实例受控:
// 用静态工厂实现实例受控的不可变颜色类 public final class Color { private final int rgb; // 缓存已创建的实例,key为rgb值,value为对应Color对象 private static final Map<Integer, Color> INSTANCE_CACHE = new HashMap<>(); // 私有构造方法,禁止外部直接new private Color(int rgb) { this.rgb = rgb; } // 静态工厂方法,负责实例创建逻辑 public static Color valueOf(int rgb) { // computeIfAbsent:如果key不存在,就用Color::new创建实例并存入缓存,然后返回;存在就直接返回缓存的实例 return INSTANCE_CACHE.computeIfAbsent(rgb, Color::new); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Color color = (Color) o; return rgb == color.rgb; } @Override public int hashCode() { return Integer.hashCode(rgb); } public int getRgb() { return rgb; } } // 测试 public class Test { public static void main(String[] args) { Color red1 = Color.valueOf(0xFF0000); Color red2 = Color.valueOf(0xFF0000); System.out.println(red1.equals(red2)); // true System.out.println(red1 == red2); // true,是同一个实例 } }
现在不管调用多少次valueOf(0xFF0000),返回的都是同一个对象,完美实现「不存在两个相等实例」的特性——只要值相等,就是同一个对象,所以equals和==的结果完全一致。
结合Boolean的例子理解
Boolean类的valueOf方法就是典型:它直接返回预定义的静态常量Boolean.TRUE和Boolean.FALSE,根本不会创建新对象。不管你调用多少次Boolean.valueOf(true),拿到的都是同一个TRUE实例,所以:
Boolean b1 = Boolean.valueOf(true); Boolean b2 = Boolean.valueOf(true); System.out.println(b1.equals(b2)); // true System.out.println(b1 == b2); // true
而如果用过时的new Boolean(true),每次都会生成新对象,==会返回false,这就是静态工厂方法和构造方法的核心区别之一——静态工厂能完全掌控实例的创建逻辑,构造方法做不到。
内容的提问来源于stack exchange,提问作者Rahul

