You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

《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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 01:39:32