首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java字节码操作'invokevirtual‘不能保持对象继承的方法的一致性

Java字节码操作'invokevirtual‘不能保持对象继承的方法的一致性
EN

Stack Overflow用户
提问于 2013-03-02 16:04:53
回答 3查看 670关注 0票数 5

我有以下代码。

代码语言:javascript
复制
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的字节码操作如下。

代码语言:javascript
复制
ALOAD 0: this
INVOKEVIRTUAL Object.toString() : String
ALOAD 0: this
INVOKEVIRTUAL Child.hashCode() : int
RETURN

我认为如果调用Object.toString(),它应该调用Parent.hashCode()来保证一致性。或者,调用Child.hashCode(),则应该调用Child.toString()。

然而,当且仅当目标方法由对象继承时,invokevirtual才不会保持其一致性。

只有在这种情况下,才会调用对象中的虚拟调用方法。对于其他情况,调用当前类中的虚拟调用方法。

我想知道为什么会这样。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-03-02 16:59:27

编译器的行为不符合逻辑的说法是正确的。但此代码的效果与您建议的两种变体的效果相同。因此,这很可能不是故意的行为,而是编译器代码长期演变的结果。其他编译器可能会产生不同的代码。

票数 4
EN

Stack Overflow用户

发布于 2013-03-02 17:52:15

根据JVM specification p. 3.7的说法

编译器不知道类实例的内部布局。相反,它会生成对实例方法的符号引用,这些引用存储在运行时常量池中。在运行时解析那些运行时常量池项,以确定实际的方法位置。

这意味着所有这些符号Child.hashCode()仅仅是常量,它们并没有指定JVM调用这些方法的方式。看来,对于toString()方法编译器来说,它知道这个方法在Object类中有它的基本实现,所以它将符号常量放在常量池中的Object类中-这是一种优化,这使得编译器适用于JVM:

代码语言:javascript
复制
  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;
票数 5
EN

Stack Overflow用户

发布于 2013-03-03 01:32:41

我的理论是:toString()使用非常频繁,所以javac使用公共的Object.toString()来保存常量池条目。

例如,如果代码包含foo.toString() and bar.toString(),则contant池只需要一个Object.toString,而不是两个条目Foo.toString and Bar.toString

Javac可能对这种优化进行了硬编码,而不是分析代码以确定是否真的需要它。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15172269

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档