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

如何让equals()判定相等的Java对象占用同一内存地址?

如何让equals相等的Java对象复用同一内存实例?

这是个很实用的内存优化思路!咱们先把核心结论说清楚:没法直接让已经创建好的两个独立对象合并成一个,但可以通过对象池/缓存的方式,从源头保证equals相等的对象只被创建一次,后续全部复用同一个实例。

为什么不能直接合并已存在的对象?

Java里每个new出来的对象都是堆内存上的独立实例,对象的引用只是指向这些实例的指针。一旦t1和t2已经指向不同的实例,你没法手动修改内存地址让这两个实例合并,只能手动把其中一个引用指向另一个(比如t2 = t1),但原来的t2实例如果没有其他引用,会被GC回收——这是手动操作,没法自动处理所有相等对象。

正确的实现方式:对象缓存池

我们可以用一个缓存容器(比如ConcurrentHashMap)来存储已经创建的对象,每次需要创建对象时,先检查缓存里有没有equals相等的对象,有就直接返回已有引用,没有再创建新对象并存入缓存。

完整示例代码

首先必须正确重写equals()和hashCode()(这是缓存能工作的核心,因为HashMap依赖这两个方法判断对象是否相等),然后用静态工厂方法封装对象创建逻辑:

import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class Test {
    // 建议把属性设为final,做成不可变类,避免缓存对象被修改导致的问题
    private final int x;
    private final String y;

    // 私有构造方法,强制通过工厂方法创建对象,避免外部直接new
    private Test(int x, String y) {
        this.x = x;
        this.y = y;
    }

    // 线程安全的缓存池,存储已创建的相等对象
    private static final ConcurrentMap<Test, Test> INSTANCE_CACHE = new ConcurrentHashMap<>();

    // 静态工厂方法:获取或创建相等的对象
    public static Test getInstance(int x, String y) {
        Test candidate = new Test(x, y);
        // computeIfAbsent:如果缓存里没有这个对象,就把它放进去,否则返回已有的
        return INSTANCE_CACHE.computeIfAbsent(candidate, k -> k);
    }

    // 重写equals,判断对象逻辑相等
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Test test = (Test) o;
        return x == test.x && Objects.equals(y, test.y);
    }

    // 重写hashCode,和equals保持一致(必须遵守:equals相等的对象hashCode必须相等)
    @Override
    public int hashCode() {
        return Objects.hash(x, y);
    }

    @Override
    public String toString() {
        return super.toString(); // 直接输出对象的内存地址标识
    }

    public static void main(String[] args) {
        Test t1 = Test.getInstance(1, "T1");
        Test t2 = Test.getInstance(1, "T1");
        
        System.out.println("t1的地址:" + t1);
        System.out.println("t2的地址:" + t2);
        System.out.println("t1和t2是同一个实例吗?" + (t1 == t2)); // 输出true
        System.out.println("t1和t2equals相等吗?" + t1.equals(t2)); // 输出true
    }
}

关键注意事项

  • 必须重写equals和hashCode:这两个方法是缓存池工作的基础,一定要保证逻辑一致——equals相等的对象,hashCode必须相等,反之不一定(但尽量让hashCode分布均匀,减少冲突)。
  • 优先做成不可变类:如果Test对象的属性可以被修改(比如有setter方法),那缓存的对象被修改后,它的equals和hashCode结果会变化,导致缓存失效甚至出现逻辑错误。所以建议把属性设为final,不提供修改方法。
  • 线程安全:示例里用ConcurrentHashMap是为了保证多线程环境下的安全,如果是单线程场景,用普通HashMap也可以。

额外补充:类似的JVM优化

其实Java里的字符串常量池就是类似的思路:String s1 = "T1"; String s2 = "T1";,s1和s2会指向同一个字符串实例,这是JVM层面帮我们做的对象池优化。

内容的提问来源于stack exchange,提问作者Fabian Brandão

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:08:37