When declaring properties, methods and classes in Java, the keyword final can be used to modify it. The final variable is a constant and can only be assigned once; the final method cannot be rewritten by a subclass; the final class cannot be inherited.
1. Final members
Declaring a final field helps the optimizer make better optimization decisions, because if the compiler knows that the value of the field will not change, it can safely cache the value in the register. The final field also provides an additional level of security by having the compiler force the field to be read-only.
1.1 About final member assignment
1) In Java, ordinary variables can be initialized by default. But variables of final type must be explicitly initialized.
2) Final members can and can only be initialized once.
3) The final member must be initialized at declaration (assigning a value directly to the final variable when it is defined) or in the constructor, and cannot be initialized elsewhere.
Example 1 Bat.java
public class Bat { final double PI = 3.14; // Assign final int i when defined; // Because it is to be initialized in the constructor, final List<Bat> list cannot be assigned here; // Because it is to be initialized in the constructor, Bat() cannot be assigned here { i = 100; list = new LinkedList<Bat>(); } Bat(int ii, List<Bat> l) { i = ii; list = l; } public static void main(String[] args) { Bat b = new Bat(); b.list.add(new Bat()); // bi=25; // b.list=new ArrayList<Bat>(); System.out.println("I=" + bi + " List Type:" + b.list.getClass()); b = new Bat(23, new ArrayList<Bat>()); b.list.add(new Bat()); System.out.println("I=" + bi + " List Type:" + b.list.getClass()); }}
result:
I=100 List Type:class java.util.LinkedListI=23 List Type:class java.util.ArrayList
There are two lines of statements in the main method that are commented out. If you remove the comment, the program will not be able to compile. This means that no matter whether it is the value of i or the type of list, once initialized, it cannot be changed. However b can specify the value of i or the type of list by reinitializing.
1.2 Invalid initialization of final reference field
It's a bit troublesome to use the final field correctly, especially for object references whose constructors can throw exceptions. Because the final field must be initialized only once in each constructor, if the constructor referenced by the final object may throw an exception, the compiler may report an error saying that the field has not been initialized. The compiler is generally smart enough to find that initialization in each branch of two mutex code branches (e.g., if...else blocks) happens only once, but it is not usually so "forgiving" to try...catch blocks.
The following code usually has problems.
class Thingie { public static Thingie getDefaultThingie() { return new Thingie(); }} public class Foo { private final Thingie thingie; public Foo() { try { thingie = new Thingie(); } catch (Exception e) { thingie = Thingie.getDefaultThingie();//Error:The final field thingie may already have been assigned } }}
You can modify this.
public class Foo { private final Thingie thingie; public Foo() { Thingie tempThingie; try { tempThingie = new Thingie(); } catch (Exception e) { tempThingie = Thingie.getDefaultThingie(); } thingie = tempThingie; }}
1.3 About final member usage
When you define a variable in a class, add the final keyword before it, that means that once this variable is initialized, it cannot be changed. The meaning of immutability here is that its value is immutable for the basic type, and its reference cannot be changed anymore for the object variable. However, objects themselves can be modified, and Java does not provide a way to make any objects constant. This limitation also fits in arrays, which are objects.
Example 2
private final int VAL_ONE=9;private static final int VAL_TWO=99;public static final int VAL_THREE=999;
Since VAL_ONE and VAL_TOW are final primitive types with compile-time values, both can be used as compile-time constants and there is no significant difference. VAL_THREE is a more typical way to define constants: defined as public, it can be used outside the package; defined as static to emphasize only one copy; defined as final to indicate that it is a constant.
The variable marked final becomes a constant, but this "constant" can only be used inside this class and cannot be used directly outside the class. However, when we use public static final to mark a constant, this constant becomes a global constant (a field that is both static and final only occupies a piece of storage space that cannot be changed). Moreover, constants defined in this way can only be assigned values when defined, and they cannot be used elsewhere.
Example 3
class Value { int i; public Value(int i) { this.i = i; }} public class FinalData { private static Random rand = new Random(); private String id; public FinalData(String id) { this.id = id; } private final int i4 = rand.nextInt(20); static final int i5 = rand.nextInt(20); public String toString() { return id + ":" + "i4:" + i4 + ", i5=" + i5; } public static void main(String[] args) { FinalData fd1 = new FinalData("fd1"); System.out.println(fd1); System.out.println("Creating new FinalData"); FinalData fd2 = new FinalData("fd2"); System.out.println(fd1); System.out.println(fd2); }}
result
fd1:i4:6, i5=3Creating new FinalDatafd1:i4:6, i5=3fd2:i4:17, i5=3
The Examples section shows the difference between defining a final value as static(i5) and non-static(i4). This difference will only appear when the value is initialized during the runtime, because the compiler treats the compiled value equally. (And they may disappear from optimization.) You will see this difference when you run the program. Note that in fd1 and fd2, the value of i5 cannot be changed by creating a second FinalData object. This is because it is static, which is initialized at loading, not every time a new object is created.
Example 4
class Value { int i; public Value(int i) { this.i = i; }} public class … { private Value v1=new Value(11); private final Value v2=new Value(22); private static final Value v3=new Value(33); …} public static void main(String[] args) { … fd1.v2.i++;// OK--Object isn't constant! fd1.v1=new Value(9);//OK--not final fd1.v2=new Value(0);//Error:Can't change reference fd1.v3=new Value(1);//Error:Can't change reference…} Variables from v1 to v3 illustrate the meaning of final references. As you can see in main(), you can't think that you can't change its value just because v2 is final. Since it is a reference, final means you can't point v2 to another new object again.
Example 5
public class … { private final int[] a={1,2,3,4,5,6}; …} public static void main(String[] args) { … for(int i=0;i<fd1.a.length;i++) fd1.a[i]++;// OK--Object isn't constant! fd1.a=new int[3];//Error:Can't change reference …} Having the same meaning for an array (it can change its value, but it cannot point to a new object), an array is another reference.
1.4 Solve the limitations of final arrays
Although array references can be declared final, elements of the array cannot. This means that neither the classes that expose public final array fields or return references to those fields through their methods are mutable.
// Not immutable -- the states array could be modified by a malicious// callerpublicclass DangerousStates { private final String[] states = new String[] { "Alabama", "Alaska", "ect" }; public String[] getStates() { return states; }}
Likewise, although an object reference can be declared as a final field, the object it references may still be mutable. If you want to create an invariant object using the final field, you must prevent references to arrays or mutable objects from "escape" your class. To do this without having to clone the array repeatedly, an easy way is to convert the array into a List.
// Immutable -- returns an unmodifiable List insteadpublicclass SafeStates { private final String[] states = new String[] { "Alabama", "Alaska", "ect" }; private final List statesAsList = new AbstractList() { public Object get(int n) { return states[n]; } public int size() { return states.length; } }; public List getStates() { return statesAsList; }}
1.5 About the use of final parameters
Another usage is to define the parameter in the method as final. For variables of basic types, this does not have any practical significance, because variables of basic types pass values when calling the method, that is, you can change the parameter variable in the method without affecting the calling statement. However, for object variables, it is very practical because object variables are passed their references when passing. In this way, your modification of object variables in the method will also affect the object variables in the calling statement. When you do not need to change the object variables as parameters in the method, explicitly using final for declaration will prevent you from unintentionally modifying and affecting the calling method.
1.6 About parameter variables in inner classes
In addition, when using parameter variables in the method in the inner class, this parameter variable must be declared final before it can be used.
Example 6 INClass.java
public class INClass { void innerClass(final String str) { class IClass { IClass() { System.out.println(str); } } IClass ic = new IClass(); } public static void main(String[] args) { INClass inc = new INClass(); inc.innerClass("Hello"); }} 2. Final method
2.1 Final method usage
1) In order to ensure that the behavior of a certain function remains unchanged during the inheritance process and cannot be overridden, the final method can be used.
2) All private and static methods in the class are naturally final.
2.2 Final and private keywords
All private methods in the class are implicitly specified to be final. Since the private method cannot be used, it cannot be overwritten.
"Override" will only appear if a method is part of the interface of the base class. That is, an object must be able to be transformed upwards into its primitive type and call the same method. If a method is private, it is not part of the interface of the base class. It's just some code hidden in the class, but with the same name. However, if a public, protected or package access method is generated in the same way in the export class, the method will not produce the "only with the same name" case that occurs in the base class. At this point, you did not override the method, just generated a new method. Since the private method cannot be touched and can be effectively hidden, nothing else needs to be considered except for the existence of the organizational structure of the class it belongs to.
3. Final class
When a class is defined as final, the class cannot be inherited. And because the final class prohibits inheritance, all methods in the final class are implicitly specified as final because they cannot be overwritten.
final is used in classes or methods to prevent links between methods from being broken. For example, suppose that the implementation of a method of class X assumes that method M will work in some way. Declaring X or M as final will prevent the derived class from redefining M in this way, causing X to work abnormally. While it might be better to implement X without these internal correlations, this is not always possible, and using final can prevent future incompatible changes.
PS: The difference between final, finally and finalize