【问题标题】:Java System.identityHashCode returning different value internallyJava System.identityHashCode 在内部返回不同的值
【发布时间】:2017-10-08 00:38:18
【问题描述】:

当我使用 this 引用在内部调用对象时,Java 方法 System.identityHashCode(...) 为对象返回不同的值,而不是在相同的变量引用上调用它对象。

class MyObject {
public void test(MyObject that){
        LOGGER.info("this hash: {}",System.identityHashCode(this));
        LOGGER.info("that hash: {}",System.identityHashCode(that));
        LOGGER.info("equals: {}",this == that);
    }
}

还有测试...

MyObject o = new MyObject();
o.test(o);

还有输出...

this hash: 263009111
that hash: 524075148
equals: false

什么会导致这种情况发生?有问题的真实对象是一个 Hibernate 实体,但我已将上述测试直接添加到代码中,并且它在特定场景中显示出相同的行为。为什么使用 this 关键字的对象会显示与引用自身不同的身份哈希码?我还通过设置对象的一些字段并确认本地字段设置为相同的值来确认引用是正确的。 那么如果引用是正确的,为什么identityHashCode(...)会返回两个不同的值,为什么“==”操作符会失败呢?我的印象是这个方法是专门为识别对同一个逻辑对象的引用而实现的?

【问题讨论】:

  • “为什么一个对象会使用 this 关键字显示不同的身份哈希码,而不是在对自身的引用上显示不同的身份哈希码”MVCE 可能会很好。
  • 通过引用是同一个对象。我可以设置对象的字段,例如 this.myField = ,然后测试 that.myField == this.myField,结果为真。不幸的是,我只能在我不拥有的代码上下文中重现它,所以我无法提供 MCVE。现在我的预感是休眠状态正在对破坏身份/此合同的实体做一些事情,但我无法弄清楚那是什么或为什么。
  • 也许这个stackoverflow.com/questions/25340606/… 增加了一些洞察力(Hibernate 为实体对象创建代理对象以支持延迟加载)
  • @DanWatson 更改一个对象的字段会影响另一个对象的字段这一事实并不一定证明每个引用都是同一个对象。一个可以简单地是一个围绕另一个的包装类:例如,如果您创建一个ArrayList a,然后创建一个List b = Collections.synchronizedList(a),对 b 的任何修改也将修改 a。但它们不是同一个对象!
  • 我认为hibernate的评论更正确。被设置的字段不是一个复杂的对象。它是一个原始字段,在两个地方都设置为完全相同的值,但只能通过一次赋值。我认为正在发生的事情是休眠 javassist 魔术正在弄乱 this 运算符,因此它从变量引用返回不同的身份。

标签: java hibernate hashcode


【解决方案1】:

它们是两个独立的对象(即使它们在概念上可能包含相同的数据),正如== 为假的事实所证明的那样。

System.identityHashCode()

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

换句话说,标准hashCode 仅使用对象的地址。每个不同的对象都会给出不同的值。这与休眠无关,System.identityHashCode() 就是这样设计的。

如果你想要你期望的行为,那么对 hibernate 返回给你的对象使用 equalshashCode 方法。

你很可能有某种树结构:

this->A<-that

无论是通过this 还是that,对A 的更改都可以通过任一引用看到,但它们仍然是两个不同的Java 对象,即使它们包装了相同的内部值。这很可能是用于支持延迟加载值之类的休眠代理对象。

要自己确认这一点,请在调试器中逐步完成并查看实际对象。

【讨论】:

  • 如果它们是两个独立的对象,那么为什么要设置一个的内部字段(通过引用而不是通过方法)总是将另一个的内部字段设置为完全相同的值?我认为它们是同一个对象,并且 hibernate 正在做一些魔术,正如其他 cmets 之一所建议的那样。
  • 不是树形结构。这是一个原始字段,并且两个字段都通过一个赋值来设置。我认为这证明它们是同一个对象?
  • @DanWatson 您自己的测试证明它们不是同一个对象。 equals: false。您认为它们是同一个对象,但显然它们不是。我不确定现场魔术是如何发生的,但没有看到代码(而且我也没有时间深入挖掘)我只能说如果== 返回 false 那么身份哈希码通常会有所不同这是意料之中的。因此,您提出的问题得到了回答。一个有趣的后续问题是修改两个不同对象的内部字段如何影响它们。我希望涉及到休眠反射魔法。
  • @TimB:“一个有趣的后续问题是修改两个不同对象的内部场如何影响它们。我希望涉及休眠反射魔法。”
  • 我在调试器中确认对象确实是分开的。即有一个 MyObject 的实例和一个 MyObject_$$_javassist_ 的实例,它是代理对象。这就解释了 IdentityHashCode 的区别。我仍然不确定代理如何将 this 引用换成新对象,但它似乎就是这样做的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-09-07
  • 1970-01-01
  • 2022-11-27
  • 2015-01-10
  • 1970-01-01
  • 1970-01-01
  • 2019-04-15
相关资源
最近更新 更多