In the java language, a final field takes an immediate value at initialisation and cannot be changed anymore. In java bytecode (jasmin), if I make a final field, it ignores the immediate value I assigned it at initialisation and I can change it like any other variable later on.
Ex. java code :
public class App{
final int CONST = 2;
App(){
CONST = 3;
}
public static void main(String[] args){
App app = new App();
}
}
Output:
App.java:4 error: cannot assign a value to final variable CONST
Ex. jasmin bytecode :
.class App
.super java/lang/Object
.field private final CONST I = 2 ;!!! the immediate value is ignored, 0 assigned
.method public <init>()V
.limit stack 3
.limit locals 1
aload_0
invokespecial java/lang/Object/<init>()V
aload_0
bipush 3
putfield App/CONST I ;!!! overwritting final field
return
.end method
.method public static main([Ljava/lang/String;)V
.limit stack 1
.limit locals 1
new App
invokespecial App/<init>()V
return
.end method
Output:
Generated: App.class
No error? I also tested printing out the new CONST value and it works just like a normal variable. Why isn't the final field working like it does in java code?
The Java language enforces a lot of constraints that are not enforced at the bytecode level. One of them is handling of final fields.
In Java bytecode, the only constraints on final fields are that static final fields cannot be assigned outside the
<clinit>
method and nonstatic final fields cannot be assigned outside the<init>
methods (i.e constructors).You can assign to a final field 0, 1, 2 or any number of times. If you chain constructors, you can assign to it in one constructor and then overwrite it in a different constructor.
To find out more about how bytecode works, you should read the JVM Specification.
On a side note, the following are completely different things, despite the deceptively similar syntax.
In the first example, this is a (nonstatic) initializer. The body of all initializers is effectively just copypasted into each of your constructors. So you get the same effect as if you had written
CONST = 2
in the constructor.By contrast, the Jasmin syntax is creating a
ConstantValue
attribute. This is normally used to give an initial value for static final fields. It can be specified for any field but is ignored for non-static fields, hence why you see the value being ignored.