How to remove the final keyword, how does the program work?

This question is not limited to strings. Out of academic curiosity, I would like to know how the final modifier for a variable can change the behavior of a program. The following example shows that this is possible.

These lines print true

 final String x = "x"; System.out.println(x + x == "xx"); 

but these lines print false

 String x = "x"; System.out.println(x + x == "xx"); 

Besides the internment of String , are there other things that can lead to a change in program behavior if the final modifier is removed from the variable declaration? I assume that the program compiles with or without a modifier.

Please do not vote to close it as a duplicate Compare strings with == which are declared final in Java . I understand the String example.

I ask if there are other reasons that eliminate the final modifier that may matter. Please, someone can contact the answer or answer the question. Thank you

+8
java final
source share
1 answer

The final modifier ensures that the variable is definitely assigned and prohibits any reassignment of this variable to and from it.

The only special cases that can be observed are explicitly specified in JLS :

A variable of primitive type or String type, which is final and initialized by the expression of the compile-time constant (§15.28), is called a constant variable.

Regardless of whether a variable is constant or not, it can have consequences for class initialization (§12.4.1), binary compatibility (§13.1, §13.4.9) and a specific assignment (§16).

There is a decent amount of JLS reading to cover the main point: JLS §13.4.9 , you may not encounter any negative consequences when deleting the final modifier.

However, according to JLS 17.5 , if you rely on the guarantee of a stream only by seeing the specifically assigned variables in an object that it can observe, then deleting the final variable will cause these variables to cease to be visible to another stream.


So, if we first look at class initialization , there are rules related to class initialization if the field is static and not a constant variable:

The class or interface type T will be initialized immediately before the first occurrence of any of the following values:

  • T is a class and an instance of T. is created.
  • T is a class, and the static method declared by T is invoked.
  • Assigned to a static field declared by T.
  • A static field declared by T is used, and the field is not a constant variable (§4.12.4).

JLS §13.1 states that changing a field to final may violate binary compatibility :

References to fields that are constant variables (§4.12.4) are resolved at compile time to the constant value that is indicated. No link to such a field should be present in the code of the binary file (except for the class or interface containing this field, which will have code to initialize it). Such a field should always be initialized (§12.4.2); the initial default value for the type of such a field should never be observed. See §13.4.9 for a discussion.

From 13.4.9:

If a field that has not been declared final is modified to be final, then it may break compatibility with pre-existing binaries that try to assign new values ​​to the field.

Removing the final keyword or changing the value of the initialized field does not violate compatibility with existing binaries.

If the field is a constant variable (§4.12.4), then deleting the final keyword or changing its value will not violate compatibility with pre-existing binaries, causing them to not start, but they will not see any new value for using the field if they are not recompiled. This is true even if use itself is not a constant expression of compilation time (§15.28).

This result is a side effect of the decision to support conditional compilation, as discussed at the end of §14.21.

Therefore, one should be careful to suddenly change the fields to final . Deleting a field is safe .

... but this only applies to the single-threaded world. From JLS 17.5 :

Fields declared final are initialized once, but never changed under normal circumstances. The detailed semantics of the final fields are somewhat different from ordinary fields. In particular, compilers have great freedom in transferring read final fields through barriers to synchronization and access to arbitrary or unknown methods. Accordingly, compilers are allowed to save the value; the final field is cached in the register and does not reload it from memory in situations in which it is necessary to reload the non-final field.

trailing fields also allow programmers to implement thread-safe immutable objects without synchronization. An object that is immutable over a stream is considered to be immutable for all threads, even if the data race is used to pass a link to an immutable object between threads. This can provide security guarantees against misuse of an immutable class with incorrect or malicious code. end fields must be used correctly to ensure consistency.

An object is considered fully initialized when its constructor is constructed. A stream that can only see a reference to an object after the object is fully initialized is guaranteed to see correctly initialized values ​​for this object's final fields.

So, if your program relies on the above guarantee that it will work fine, then deleting the final keyword will have consequences for streaming.

+5
source share

All Articles