当toString()和hashCode()被覆盖时,如何在Java中获取对象的"object reference"?

132

我希望能够在Java中打印对象的“对象引用”以进行调试。 也就是说,为了确保对象在不同情况下是否相同。

问题在于,所涉及的类继承自另一个类,该类已覆盖了通常会给出对象标识符的toString()和hashCode()方法。

例如的情况是: 运行多线程应用程序时,在开发期间,我想检查所有线程是否使用同一资源对象实例。


1
根据你是否能够做到...== 是正确的方式...但是我不知道所讨论代码的结构如何。再次,对于你正在做的事情,hashCode 很可能是适用的,但它可能会因库的实现方式而出错。 - TofuBeer
1
这确实是一个很好的问题。 - Zhang Xiang
6个回答

122

你计划用它做什么(你想要做什么会影响你需要调用什么)。

hashCode,根据JavaDocs的定义,如下所示:

尽可能合理,Object类定义的hashCode方法会为不同的对象返回不同的整数。(通常通过将对象的内部地址转换为整数来实现,但这种实现技术并非Java编程语言所必需)

所以如果你使用hashCode()来查找它是否是存储在内存中的唯一对象,那么这并不是一个好办法。

System.identityHashCode的功能如下:

无论给定对象的类是否覆盖hashCode()方法,返回与默认方法hashCode()返回的给定对象的哈希码相同的哈希码。null引用的哈希码为零。

就你所要做的而言,这听起来像是你想要的...但是,根据库的实现方式,你想要做什么可能并不安全。


6
我没有根据代码中的价值进行操作。根据我的问题编辑,我只是将其用于调试某种情况下的目的。 这就是为什么我觉得我的回答是合理的,但我给你的回复点赞表示很有见地。 - Nicolai
1
可能性是它总是会按照你的意愿执行 - 但在某些虚拟机上可能会出现故障。 - TofuBeer
在任何合适的虚拟机上,它都会崩溃(即 identityHashCode 不一定是唯一的)。identityHashCode 不是一个标识符。 - Tom Hawtin - tackline
如先前提到的,哈希码并不保证是基于地址的。我曾经在 WAS 中的 IBM VM 中看到具有相同 ID 的多个对象出现。 - Robin
通常情况下,这是通过将对象的内部地址转换为整数来实现的,这是Sun的默认实现,但不能保证。例如s =“Hello”和t =“Hello”,它们可能会导致s和t具有相同的identityHashCode,因为它们实际上是同一个对象。 - TofuBeer
@TofuBeer:它们肯定会得到相同的值,因为它们是由JLS和/或JVM规范定义的相同对象(我总是记得哪一个)。 - Joachim Sauer

59

这是我的解决方案:

Integer.toHexString(System.identityHashCode(object));

7
因为多个对象可能返回相同的IdentityHashCode,所以这并不完全正确。 - Robin
2
两个具有相同标识哈希值的对象(引用)不是同一个对象吗?这就是 OP 想要的。 - basszero
4
不,这不是真的。很有可能,但不能保证,因为规范没有定义算法。 - Robin

10

双等号==会始终根据对象的身份标识进行检查,而不考虑对象对hashCode或equals方法的实现。当然,确保您比较的对象引用是volatile(在1.5+的JVM中)。

如果您真的需要原始Object toString结果(尽管这并不是您示例用例的最佳解决方案),Commons Lang库有一个方法ObjectUtils.identityToString(Object)可以满足您的要求。从JavaDoc中得知:

public static java.lang.String identityToString(java.lang.Object object)
获取一个对象的 toString() 方法在该类没有自己的实现时默认返回的值。如果该对象为 null,则返回 null。
 ObjectUtils.identityToString(null)         = null
 ObjectUtils.identityToString("")           = "java.lang.String@1e23"
 ObjectUtils.identityToString(Boolean.TRUE) = "java.lang.Boolean@7fa"

2
如果您正在使用Java 7,则应考虑使用java.util.Objects - noahlz
2
关于您的评论,您在 java.util.Objects 中想到了哪些方法?Objects.toString(o) 相当于调用 o.toString(),并进行了空值检查,这似乎与此问题无关。 - amaidment
1
@amaidment 好的,是的,Object.toString只是调用引用上的toString方法,避免了NPEs。 - noahlz

5
由于默认的hashCode()方法可能不返回地址,并且可能存在具有相同hashCode的多个对象,因此您无法安全地实现所需功能。要实现您想要的功能,唯一的方法是实际上覆盖相关对象的hashCode()方法并确保它们都提供唯一的值。这是否可行取决于您的情况。
值得一提的是,在运行在WAS服务器中的IBM VM中,我曾经遇到过具有相同默认哈希码的多个对象。我们有一个缺陷,因为这些对象被放入远程缓存中而被覆盖。那时对我来说是个启示,因为我认为默认的hashCode()方法也是对象内存地址。

3

为所有实例添加唯一的id,例如:

public interface Idable {
  int id();
}

public class IdGenerator {
  private static int id = 0;
  public static synchronized int generate() { return id++; }
}

public abstract class AbstractSomething implements Idable {
  private int id;
  public AbstractSomething () {
    this.id = IdGenerator.generate();
  }
  public int id() { return id; }
}

继承自AbstractSomething并查询此属性。在单个虚拟机内是安全的(假设没有使用类加载器绕过静态内容进行游戏操作)。


在这种情况下,我可能会使用AtomicInteger - 它具有更高的吞吐量,因为不需要同步,并且它使用由sun.misc.Unsafe提供的本机原子内存操作。 - randers

2
我们可以简单地从Object类的toString方法中复制代码来获取字符串的引用。
class Test
{
  public static void main(String args[])
  {
    String a="nikhil";     // it stores in String constant pool
    String s=new String("nikhil");    //with new stores in heap
    System.out.println(Integer.toHexString(System.identityHashCode(a)));
    System.out.println(Integer.toHexString(System.identityHashCode(s)));
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接