我有以下代码。
public class Parent {
@Override
public int hashCode() {
return 0;
}
}
public class Child extends Parent {
public void test() {
this.toString();
this.hashCode();
}
}正如您在上面的代码中看到的,子代从Object继承toString(),从父代继承hashCode()。Child#test的字节码操作如下。
ALOAD 0: this
INVOKEVIRTUAL Object.toString() : String
ALOAD 0: this
INVOKEVIRTUAL Child.hashCode() : int
RETURN我认为如果调用Object.toString(),它应该调用Parent.hashCode()来保证一致性。或者,调用Child.hashCode(),则应该调用Child.toString()。
然而,当且仅当目标方法由对象继承时,invokevirtual才不会保持其一致性。
只有在这种情况下,才会调用对象中的虚拟调用方法。对于其他情况,调用当前类中的虚拟调用方法。
我想知道为什么会这样。
发布于 2013-03-02 16:59:27
编译器的行为不符合逻辑的说法是正确的。但此代码的效果与您建议的两种变体的效果相同。因此,这很可能不是故意的行为,而是编译器代码长期演变的结果。其他编译器可能会产生不同的代码。
发布于 2013-03-02 17:52:15
根据JVM specification p. 3.7的说法
编译器不知道类实例的内部布局。相反,它会生成对实例方法的符号引用,这些引用存储在运行时常量池中。在运行时解析那些运行时常量池项,以确定实际的方法位置。
这意味着所有这些符号Child.hashCode()仅仅是常量,它们并没有指定JVM调用这些方法的方式。看来,对于toString()方法编译器来说,它知道这个方法在Object类中有它的基本实现,所以它将符号常量放在常量池中的Object类中-这是一种优化,这使得编译器适用于JVM:
Constant pool:
const #2 = Method #24.#25; // java/lang/Object.toString:()Ljava/lang/String;
...
const #24 = class #34; // java/lang/Object
const #25 = NameAndType #35:#36;// toString:()Ljava/lang/String;发布于 2013-03-03 01:32:41
我的理论是:toString()使用非常频繁,所以javac使用公共的Object.toString()来保存常量池条目。
例如,如果代码包含foo.toString() and bar.toString(),则contant池只需要一个Object.toString,而不是两个条目Foo.toString and Bar.toString
Javac可能对这种优化进行了硬编码,而不是分析代码以确定是否真的需要它。
https://stackoverflow.com/questions/15172269
复制相似问题