Final methods in java cannot be overridden by the inherited class.
But why the final methods are invoked using invokevirtual
instead of invokespecial
.
Final methods in java cannot be overridden by the inherited class.
But why the final methods are invoked using invokevirtual
instead of invokespecial
.
The semantics of the final
modifier are only enforced by the bytecode compiler.
The link to JavaSE7 specification
Some other samples about final
keyword.
The difference between the invokespecial and the invokevirtual instructions is that invokevirtual invokes a method based on the class of the object. The invokespecial instruction is used to invoke instance initialization methods as well as private methods and methods of a superclass of the current class.
When we invoke a final method, corresponding bytecode instruction is INVOKEVIRTUAL
, the same as other non-final method.
final
keyword.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
// 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
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?
And rest assured, in practice this lookup is optimized away during linkage - at least in HotSpot.
The fact final instance methods are treated as virtual rather than special seems like a technicality of the 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.
"Changing a method that is declared
final
to no longer be declaredfinal
does not break compatibility with pre-existing binaries. "
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
. That violates the rule above.
But as the other answers / comments have already noted, the JIT compiler will choose the best calling sequence anyway. Even if you don't declare a method as final
, the JIT compiler can figure out when no vtable dispatching is necessary. (And it can even recompile methods when a new class is loaded that invalidates the preconditions.)