当前位置: 首页 > 面试题库 >

为什么字节码在直接字段访问中调用Object-> getClass()

焦驰
2023-03-14
问题内容

反编译了Java(实际上是Dalvik)字节码。在方法开始时,我直接访问实例成员的字段(即,不通过getter进行访问)。

似乎Java调用Object.getClass()了访问的实例成员(mOther),但未在任何地方使用结果。这是某种检查吗?为什么需要这个电话?我怀疑这是因为我直接访问一个字段(在该类中定义),但是看不到连接。

Java代码和反编译的字节码如下。

(请注意,最后一条指令加载lifeTime为常量,0x0001因为在中MyOtherClass,我有lifeTime一个public final字段,并且当前是从代码初始化的。)

MyOtherClass other = mOther;
if (mAge >= other.lifeTime) { // lifeTime is initialized to 0x0001
   end();
   return;
}

.line 53
move-object/from16 v0, p0
iget-object v0, v0, Lcom/example/engine/MyClass1;->mOther:Lcom/example/engine/MyOtherClass;
move-object/from16 v16, v0

.line 54
.local v16, other:Lcom/example/engine/MyOtherClass;
move-object/from16 v0, p0

iget v0, v0, Lcom/example/engine/MyClass1;->mAge:I
move/from16 v18, v0

// Why is Object->getClass() called?
invoke-virtual/range {v16 .. v16}, Ljava/lang/Object;->getClass()Ljava/lang/Class;

const/16 v19, 0x0001

更新:

在评论中有人要求我提供该方法的完整源代码。请注意,这mOther是最后一个字段(出于性能原因)。您在这里:

@Override
public void doStep() {
    MyOtherClass other = mOther;
    if (mAge >= other.lifeTime) {
        end();
        return;
    }
    mAge += TICK_TIME;

    boolean isSurrounded = false;
    if (mAge > mLastSurroundTime + other.surroundingTime) {
        int distance = (int)other.maxSurroundDistance;

        for (int bx = bx0; bx <= bx1; ++bx) {
            if (bx < 0 || bx >= mSize) { continue; }
            for (int by = by0; by <= by1; ++by) {
                if (by < 0 || by >= mSize) { continue; }
                ArrayList<WorldObject> candidates = getCandidatesAtPos(bx, by);
                for (int i = 0; i < candidates.size(); ++i) {
                    WorldObject obj = candidates.get(i);
                    if (mSelf!= obj && mSelf.getDistanceFrom(obj) <= other.maxSurroundDistance) {
                        obj.notifyDangerImminent(mSelf);
                        isSurrounded = true;
                    }
                }
            }
        }
        if (isSurrounded) { mLastSurroundTime = mAge; }
    }
}

问题答案:

我假设lifeTime是在声明时分配的最终字段:

 final int lifeTime = 0x0001;

如果是这样,则按以下方式优化字节码(它与VM几乎没有关系,纯编译器不可思议):

  • 无需真正从内存中获取数据:所需要做的只是加载常量1。
  • 但是,如果该字段的所有者恰好为 空,该 怎么办?在这种情况下,必须抛出NullPointerException。为了保证这种行为,编译器发出对getClass()的调用,因为
    • 实际检查是否为空,构造一个新的NullPointerException实例并将其抛出是 很多 字节代码,
    • 这样的调用在VM中进行了非常优化,
    • 此方法始终可用,
    • 它不需要任何参数。

一个简单的例子:

class Test {
    private final int myFinalField = 1;

    int test(Test t) {
        return t.myFinalField;
    }
}

如果我们看一下test()方法的字节码(这次是JVM,但是如果您将其转换为Dalvik,则本质上是相同的),这也是对getClass()的调用:

 // access flags 0x0
  test(LTest;)I
   L0
    LINENUMBER 5 L0

    // load t
    ALOAD 1

    // if (t == null) throw new NullPointerException(); compressed in only two instructions
    INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
    POP

    // the actual value of myFinalField
    ICONST_1

    IRETURN
   L1
    LOCALVARIABLE this LTest; L0 L1 0
    LOCALVARIABLE t LTest; L0 L1 1
    MAXSTACK = 1
    MAXLOCALS = 2


 类似资料:
  • 问题内容: 在大学里学习时,我不得不做一些难看的Java基础知识,例如不使用封装就可以工作,同一类中的主要方法等。(我不想在Java样式指南上展开讨论,我只是想澄清一下,我不会在大学以外写这样的东西) 我偶然发现了一种我无法向自己解释的行为: 为什么这段代码可以编译并正确运行?我怎么可能访问私有字段?由于主类位于同一类中,因此行为异常? 问题答案: 由于静态方法是类的成员,因此可以访问中的任何私有

  • 我的问题是为什么找不到“name”字段?

  • 我正在修改我以前使用的Visitor模式。我们有基类Element,它有虚拟方法接受(Visitor),并且这个方法在继承自Element的所有类中都被重写。在任何派生类中,接受()所做的就是调用visitor- 为什么客户不能直接打电话给访客- 调用元素中有哪些有用的信息。接受(访客),然后依次呼叫访客。参观(元素)?这使得Visitor模式的使用很麻烦,并且在所有元素类的层次结构中都需要额外的

  • 问题内容: 为什么Java编译器不会让我把成,是8位长,而这正大小的数据类型。 有人可以解释为什么1有效,为什么2不有效吗? 编辑 我读了答案,声称0xff是255,怎么办?不是吗,是什么导致0xff,-128或255或其他原因。为什么不将其视为字节,而不是将该字节的8位视为1。 问题答案: 在Java 类型是一个8位有符号整数类型与在范围内的值来。文字表示哪个超出了该范围。 在第一个示例中,您试

  • 问题内容: 当我从中读取源代码时,我很困惑为什么它编写了这样的代码: 为什么使用别名而不是直接使用field变量,如下所示: 有人可以给出合理的解释吗? 问题答案: 如果您是从上下文中看这段代码,那么对于“别名”将没有很好的解释。它仅仅是冗余代码或不良的代码样式。 但是上下文是可以被子类化的类,它需要在多线程上下文中工作。 线索是,在宣布IS 。这意味着子类有可能进入并分配给。考虑到这种可能性,实