简体   繁体   English

为什么使用 invokevirtual 操作码调用 java 中的最终方法

[英]Why final methods in java are invoked using invokevirtual opcode

Final methods in java cannot be overridden by the inherited class. java 中的最终方法不能被继承的 class 覆盖。

But why the final methods are invoked using invokevirtual instead of invokespecial .但是为什么使用invokevirtual而不是invokespecial来调用最终方法。

The semantics of the final modifier are only enforced by the bytecode compiler . final修饰符的语义仅由字节码编译器强制执行。

The link to JavaSE7 specification Some other samples about final keyword. JavaSE7规范的链接 关于final关键字的一些其他示例。

The difference between the invokespecial and the invokevirtual instructions is that invokevirtual invokes a method based on the class of the object. invokespecial和invokevirtual指令之间的区别在于invokevirtual基于对象的类调用方法。 The invokespecial instruction is used to invoke instance initialization methods as well as private methods and methods of a superclass of the current class. invokespecial指令用于调用实例初始化方法以及当前类的超类的私有方法和方法。

When we invoke a final method, corresponding bytecode instruction is INVOKEVIRTUAL , the same as other non-final method. 当我们调用final方法时,相应的字节码指令是INVOKEVIRTUAL ,与其他非final方法相同。

Here is a example of invoking method with final keyword. 以下是使用final关键字调用方法的示例。

CounterPoint.java CounterPoint.java

public class CounterPoint extends Point {

    private static final AtomicInteger counter = new AtomicInteger();

    public CounterPoint(int x, int y) {
        super(x, y);
        counter.incrementAndGet();
    }

    public static String numberCreated() {
        return counter.toString();
    }

}

ByteCode of CounterPoint CounterPoint的ByteCode

// class version 52.0 (52)
// access flags 0x21
public class com/xetrasu/CounterPoint extends java/awt/Point  {

  // compiled from: CounterPoint.java

  // access flags 0x1A
  private final static Ljava/util/concurrent/atomic/AtomicInteger; counter

  // access flags 0x1
  public <init>(II)V
   L0
    LINENUMBER 11 L0
    ALOAD 0
    ILOAD 1
    ILOAD 2
    INVOKESPECIAL java/awt/Point.<init> (II)V
   L1
    LINENUMBER 12 L1
    GETSTATIC com/xetrasu/CounterPoint.counter : Ljava/util/concurrent/atomic/AtomicInteger;
    INVOKEVIRTUAL java/util/concurrent/atomic/AtomicInteger.incrementAndGet ()I
    POP
   L2
    LINENUMBER 13 L2
    RETURN
   L3
    LOCALVARIABLE this Lcom/xetrasu/CounterPoint; L0 L3 0
    LOCALVARIABLE x I L0 L3 1
    LOCALVARIABLE y I L0 L3 2
    MAXSTACK = 3
    MAXLOCALS = 3

  // access flags 0x9
  public static numberCreated()Ljava/lang/String;
   L0
    LINENUMBER 16 L0
    GETSTATIC com/xetrasu/CounterPoint.counter : Ljava/util/concurrent/atomic/AtomicInteger;
    INVOKEVIRTUAL java/util/concurrent/atomic/AtomicInteger.toString ()Ljava/lang/String;
    ARETURN
    MAXSTACK = 1
    MAXLOCALS = 0

  // access flags 0x8
  static <clinit>()V
   L0
    LINENUMBER 8 L0
    NEW java/util/concurrent/atomic/AtomicInteger
    DUP
    INVOKESPECIAL java/util/concurrent/atomic/AtomicInteger.<init> ()V
    PUTSTATIC com/xetrasu/CounterPoint.counter : Ljava/util/concurrent/atomic/AtomicInteger;
    RETURN
    MAXSTACK = 2
    MAXLOCALS = 0
}

AtomicInteger.java AtomicInteger.java

public class AtomicInteger extends Number implements java.io.Serializable {
    public String toString() {
        return Integer.toString(get());
    }
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }
}

Sounds like what you're actually asking is:听起来您实际上要问的是:

Why would we need a vtable lookup for a method that could be linked statically?为什么我们需要对可以静态链接的方法进行vtable查找?

And rest assured, in practice this lookup is optimized away during linkage - at least in HotSpot .并且 rest 保证,在实践中,这种查找在链接期间被优化 - 至少在 HotSpot 中

The fact final instance methods are treated as virtual rather than special seems like a technicality of the JVMS.最终实例方法被视为虚拟而不是特殊的事实似乎是 JVMS 的技术性问题。

It is necessary to use INVOKEVIRTUAL to call a final method in order to implement the binary compatibility rule in JLS 13.4.17 .为了实现JLS 13.4.17中的二进制兼容性规则,需要使用INVOKEVIRTUAL调用final方法。

"Changing a method that is declared final to no longer be declared final does not break compatibility with pre-existing binaries. " “将声明为final的方法更改为不再声明为final不会破坏与预先存在的二进制文件的兼容性。”

If calls to final method were compiled to INVOKESPECIAL , then those calls would need to be recompiled if the method was changed to not be final .如果对final方法的调用编译为INVOKESPECIAL ,那么如果方法更改为非final ,则需要重新编译这些调用。 That violates the rule above.这违反了上面的规则。

But as the other answers / comments have already noted, the JIT compiler will choose the best calling sequence anyway.但正如其他答案/评论已经指出的那样,JIT 编译器无论如何都会选择最佳调用序列。 Even if you don't declare a method as final , the JIT compiler can figure out when no vtable dispatching is necessary.即使您没有将方法声明为final ,JIT 编译器也可以确定何时不需要 vtable 调度。 (And it can even recompile methods when a new class is loaded that invalidates the preconditions.) (它甚至可以在加载新的 class 使前提条件无效时重新编译方法。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM