I have the following class:
public class Test {
private int a;
public Test(final int a) {
this.a = a;
}
}
I tried compiling (using javac Test.java
) with and without the final
keyword on a
. In both cases, when I decompile (using javap -v -c Test.class
) I get the following bytecode (java 11, open jdk):
Classfile /home/sadeep/repos/podium.cubs-cnt-svc-modelruntime/src/main/java/podium/cubs/cnt/svc/modelruntime/Test.class
Last modified Nov 3, 2021; size 261 bytes
MD5 checksum aa55c5cbaad8a25b1ebf28a572fa72e3
Compiled from "Test.java"
public class podium.cubs.cnt.svc.modelruntime.Test
minor version: 0
major version: 55
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #3 // podium/cubs/cnt/svc/modelruntime/Test
super_class: #4 // java/lang/Object
interfaces: 0, fields: 1, methods: 1, attributes: 1
Constant pool:
#1 = Methodref #4.#13 // java/lang/Object."<init>":()V
#2 = Fieldref #3.#14 // podium/cubs/cnt/svc/modelruntime/Test.a:I
#3 = Class #15 // podium/cubs/cnt/svc/modelruntime/Test
#4 = Class #16 // java/lang/Object
#5 = Utf8 a
#6 = Utf8 I
#7 = Utf8 <init>
#8 = Utf8 (I)V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 SourceFile
#12 = Utf8 Test.java
#13 = NameAndType #7:#17 // "<init>":()V
#14 = NameAndType #5:#6 // a:I
#15 = Utf8 podium/cubs/cnt/svc/modelruntime/Test
#16 = Utf8 java/lang/Object
#17 = Utf8 ()V
{
public podium.cubs.cnt.svc.modelruntime.Test(int);
descriptor: (I)V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: iload_1
6: putfield #2 // Field a:I
9: return
LineNumberTable:
line 6: 0
line 7: 4
line 8: 9
}
SourceFile: "Test.java"
Is final
on method parameters not a jvm construct?
EDIT:
javap -p -c -v
:
Classfile /home/sadeep/Test.class
Last modified Nov 3, 2021; size 228 bytes
MD5 checksum 2f3a79a3c91a62a2d7831651be24168b
Compiled from "test.java"
class Test
minor version: 0
major version: 55
flags: (0x0020) ACC_SUPER
this_class: #3 // Test
super_class: #4 // java/lang/Object
interfaces: 0, fields: 1, methods: 1, attributes: 1
Constant pool:
#1 = Methodref #4.#13 // java/lang/Object."<init>":()V
#2 = Fieldref #3.#14 // Test.a:I
#3 = Class #15 // Test
#4 = Class #16 // java/lang/Object
#5 = Utf8 a
#6 = Utf8 I
#7 = Utf8 <init>
#8 = Utf8 (I)V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 SourceFile
#12 = Utf8 test.java
#13 = NameAndType #7:#17 // "<init>":()V
#14 = NameAndType #5:#6 // a:I
#15 = Utf8 Test
#16 = Utf8 java/lang/Object
#17 = Utf8 ()V
{
private final int a;
descriptor: I
flags: (0x0012) ACC_PRIVATE, ACC_FINAL
public Test(int);
descriptor: (I)V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: iload_1
6: putfield #2 // Field a:I
9: return
LineNumberTable:
line 3: 0
line 4: 4
line 5: 9
}
SourceFile: "test.java"
Looks like even with -p, the final indicator is only set for the field.
CodePudding user response:
TL;DR: You don’t need that information in the bytecode
The final
keyword on a parameter means that you cannot assign a value to that parameter inside your method or constructor. It’s the compiler’s job to check that you are not doing this (and the compiler will issue an error message if you do). Once the compiler has done its job, we can be assured that the parameter indeed keeps its initial value throughout. There is no need to have the information in the bytecode that the parameter is declared final
. Which is why the information is not there.
From a comment by Link182:
Then how do we know the parameter is final in a compiled library?
You don’t. You don’t need to. What would you use that information for? It’s only relevant for the implementor of the method.
CodePudding user response:
The MethodParameters attribute is used to indicate that parameters are final. https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-4.html#jvms-4.7.24
In order for javac to add this attribute, you need to pass the -parameters
option.