In Java we see lots of places where the final
keyword can be used but its use is uncommon.
For example:
String str = \"abc\";
System.ou
I am amazed that no one has actually posted some real code that is de-compiled to prove that there is at least some minor difference.
For the reference this has been tested against javac
version 8
, 9
and 10
.
Suppose this method:
public static int test() {
/* final */ Object left = new Object();
Object right = new Object();
return left.hashCode() + right.hashCode();
}
Compiling this code as it is, produces the exact same byte code as when final
would have been present (final Object left = new Object();
).
But this one:
public static int test() {
/* final */ int left = 11;
int right = 12;
return left + right;
}
Produces:
0: bipush 11
2: istore_0
3: bipush 12
5: istore_1
6: iload_0
7: iload_1
8: iadd
9: ireturn
Leaving final
to be present produces:
0: bipush 12
2: istore_1
3: bipush 11
5: iload_1
6: iadd
7: ireturn
The code is pretty much self-explanatory, in case there is a compile time constant, it will be loaded directly onto the operand stack (it will not be stored into local variables array like the previous example does via bipush 12; istore_0; iload_0
) - which sort of makes sense since no one can change it.
On the other hand why in the second case the compiler does not produce istore_0 ... iload_0
is beyond me, it's not like that slot 0
is used in any way (it could shrink the variables array this way, but may be Im missing some internals details, can't tell for sure)
I was surprised to see such an optimization, considering how little ones javac
does. As to should we always use final
? I'm not even going to write a JMH
test (which I wanted to initially), I am sure that the diff is in the order of ns
(if possible to be captured at all). The only place this could be a problem, is when a method could not be inlined because of it's size (and declaring final
would shrink that size by a few bytes).
There are two more final
s that need to be addressed. First is when a method is final
(from a JIT
perspective), such a method is monomorphic - and these are the most beloved ones by the JVM
.
Then there are final
instance variables (that must be set in every constructor); these are important as they will guarantee a correctly published reference, as touched a bit here and also specified exactly by the JLS
.
That being said : there is one more thing that is invisible to every single answer here: garbage collection
. It is going to take a lot of time to explain, but when you read a variable, a GC
has a so-called barrier
for that read. Every aload
and getField
is "protected" via such a barrier, a lot more details here. In theory, final
fields do not need such a "protection" (they can skip the barrier entirely). So if a GC does that - final
will improve performance.
Final (At least for member variables and parameters) is more for humans then it is for the machine.
It's good practice to make variables final wherever possible. I wish Java had made "variables" final by default and had a "Mutable" keyword to allow changes. Immutable classes lead to much better threaded code, and just glancing at a class with "final" in front of each member will quickly show it to be immutable.
Another case--I've been converting a lot of code to use @NonNull/@Nullable annotations (You can say a method parameter must not be null then the IDE can warn you every place you pass a variable that isn't tagged @NonNull--the whole thing spreads to a ridiculous degree). It's much easier to prove a member variable or parameter can't be null when it's tagged final since you know it's not being re-assigned anywhere else.
My suggestion is to get in the habit of applying final for members and parameters by default, It's just a few characters but will nudge you towards improving your coding style if nothing else.
Final for methods or classes is another concept since it disallows a very valid form of reuse and doesn't really tell the reader much. The best use is probably the way they made String and the other intrinsic types final so you could rely on consistent behavior everywhere--That prevented a lot of bugs (although there are times I would have LOVED to extend string.... oh the possibilities)
As mentioned elsewhere, 'final' for a local variable, and to a slightly lesser extent a member variable, is more a matter of style.
'final' is a statement that you intend the variable to not change (i.e., the variable won't vary!). The compiler can then help you out by complaining if you violate your own constraint.
I share the sentiment that Java would have been a better language if identifiers (I'm sorry, I just cannot call a non-varying thing a 'variable') were final by default, and required you to explicitly say that they were variables. But having said that, I don't generally use 'final' on local variables that are initialized and never assigned; it just seems too noisy.
(I do use final on member variables)
YES it can. Here is an instance where final can boost performance:
Conditional compilation is a technique in which lines of code are not compiled into the class file based on a particular condition. This can be used to remove tons of debugging code in a production build.
consider the following:
public class ConditionalCompile {
private final static boolean doSomething= false;
if (doSomething) {
// do first part.
}
if (doSomething) {
// do second part.
}
if (doSomething) {
// do third part.
}
if (doSomething) {
// do finalization part.
}
}
By converting the doSomething attribute into a final attribute, you have told the compiler that whenever it sees doSomething, it should replace it with false as per the compile-time substitution rules. The first pass of the compiler changes the code to something like this:
public class ConditionalCompile {
private final static boolean doSomething= false;
if (false){
// do first part.
}
if (false){
// do second part.
}
if (false){
// do third part.
}
if (false){
// do finalization part.
}
}
Once this is done, the compiler takes another look at it and sees that there are unreachable statements in the code. Since you are working with a top-quality compiler, it doesn't like all those unreachable byte codes. So it removes them, and you end up with this:
public class ConditionalCompile {
private final static boolean doSomething= false;
public static void someMethodBetter( ) {
// do first part.
// do second part.
// do third part.
// do finalization part.
}
}
thus reducing any excessive codes, or any unnecessary conditional checking.
Edit: As an example, let's take the following code:
public class Test {
public static final void main(String[] args) {
boolean x = false;
if (x) {
System.out.println("x");
}
final boolean y = false;
if (y) {
System.out.println("y");
}
if (false) {
System.out.println("z");
}
}
}
When compiling this code with Java 8 and decompiling with javap -c Test.class
we get:
public class Test {
public Test();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static final void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: ifeq 14
6: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
9: ldc #22 // String x
11: invokevirtual #24 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
14: iconst_0
15: istore_2
16: return
}
We can note that compiled code includes only the non-final variable x
.
This prooves that final variables have impact on performances, at least for this simple case.
Members declared with final will be available throughout the program because unlike nonfinal ones, if these members have not been used in program, still they would not be taken care by Garbage Collector so can cause performance issue due to bad memory management.
final
keyword can be used in five ways in Java.
A class is final: a class is final means we cannot be extended or inheritance means inheritance is not possible.
Similarly - A object is final: some time we does not modified the internal state of object so in such case we can specify the object is final object.object final means not variable also final.
Once reference variable is made final, it cannot be reassigned to other object. But can change the contents of the object as long as its fields are not final