The various uses and meanings of final

Final is a keyword in Java, and in short, final means “this cannot be changed.”

However, because the final keyword can be used in three ways to modify variables, methods, or classes, and can have different effects, meanings, and emphasis in different places, we need to separate these three cases.

Final modifier variable

The final modifier is explicit. It means that the variable cannot be modified once it has been assigned, that is, it can only be assigned once until the end of the world. If we try to reassign to a variable that has already been assigned final, we will get a compilation error.

public class FinalVariable { public final int objVar = 0; public static final int clsVar = 0; public static void main(String[] args) { FinalVariable finalVariable = new FinalVariable(); finalVariable.objVar = 1; clsVar = 1; }}Copy the code

In this case, we have two final in modifiers, and attempts to modify the value in main will result ina compilation error, so this illustrates one of the main uses of final modifiers: they cannot be modified once assigned.

purpose

After seeing what it does, let’s look at the purpose of using final. Why do we add the final keyword to a variable? There are two main purposes.

  1. The first purpose is for design reasons. For example, if we want to create a quantity that cannot be changed once it is assigned, we can use the final keyword. When you declare a constant, for example, it’s usually final
  2. The second purpose is from a thread-safety perspective. Immutable objects are inherently thread-safe, so we don’t need to do extra synchronization and so on, and there’s no overhead. If final modiifies primitive data types, then it is inherently immutable, so it is automatically thread-safe, so we can be very comfortable using it in the future.

Assignment time

Let’s take a look at the assignment timing of variables modified by final. Variables can be divided into the following three types:

  1. A member variable, a non-static modified property of a class;
  2. A static variable, a property of a class that is modified static;
  3. Local variables, variables in methods.
Member variables

A member variable is a nonstatic property ina class that has three assignment opportunities (or assignment paths) after final modification.

  1. The first is to assign directly to the right of the equal sign of the declared variable, for example:
public class FinalFieldAssignment1 {
    private final int finalVar = 0;
}
Copy the code
  1. The second way is to assign a value in the constructor, for example:
class FinalFieldAssignment2 { private final int finalVar; public FinalFieldAssignment2() { finalVar = 0; }}Copy the code
  1. The third way is to assign in the constructor block of the class
class FinalFieldAssignment3 { private final int finalVar; { finalVar = 0; }}Copy the code

It is important to note that there are three assignment times and we must choose one of them to complete the assignment to the final variable. If it is not a final ordinary variable, it can be assigned at other times rather than in these three cases; Or if you’re not going to use the variable, it might even be ok not to assign at all. However, for a final modified member variable, it must be assigned in one of three cases, not in all, as final syntax dictates.

Here’s a concept: “blank final.” If we declare a final variable and do not immediately assign it a value to the right of the equal sign, this is called “blank final”. The advantage of this is that final variables have more flexibility. For example, you can assign different values to final variables in the constructor, so that the variables that are modified by final do not become rigid, but remain unchanged after assignment.

*/ public class BlankFinal {// BlankFinal private final int a; Public BlankFinal() {this.a = 0; // BlankFinal() {this.a = 0; Public BlankFinal(int a) {this.a = a; }}Copy the code

In this code, we have a private final int variable called a. This class has two constructors. The first constructor assigns a to 0, and the second constructor assigns a to the parameter passed in.

In this way, using this rule, we can design more flexible assignment logic for final variables depending on the business. So one of the great things about using blank final is that the final variable’s value is not rigid, it’s not absolutely fixed, it can be assigned flexibly, but once it’s assigned, it can’t be changed.

A static variable

A static variable is a static property ina class that, when modified by final, has only two assignment opportunities.

The first option is to assign the value directly to the right of the equal sign of the declared variable, for example:

*/ public class StaticFieldAssignment1 {private static final int a = 0; }Copy the code

The second type of assignment opportunity is that it can be assigned within a static initial block of code. There are not many uses, such as:

class StaticFieldAssignment2 { private static final int a; static { a = 0; }}Copy the code

In this class we have a variable private static final int A, and then we have a static final int a, and then we have braces, and that’s the syntax for the static initial block, and in this case we assign a, and that’s when we allow it. These are the two times when static final variables can be assigned.

Note that we cannot assign static final variables using normal non-static initial code blocks. Also special is the fact that static final variables cannot be assigned in constructors.

A local variable

A local variable is a variable ina method, and if you modify it to final, it still means that it cannot be changed once assigned.

However, its assignment timing is different from the first two variables. Because it is defined in a method, it has no constructor, and there is no initial code block, so neither assignment timing exists.

In fact, there is no specific assignment timing for a final local variable, only that it must be assigned before it is used.

This requirement is the same as that of a non-final variable ina method, which requires that an ordinary variable ina method be assigned a value before it is used. Let’s look at an example of this code:

*/ public class LocalVarAssignment1 {public void foo() {final int a = 0; / / Public class LocalVarAssignment1 {public void foo() {final int a = 0; }} class LocalVarAssignment2 {public void foo() {final int a; Class LocalVarAssignment3 {public void foo() {final int a;} class LocalVarAssignment3 {public void foo() {final int a; a = 0; System.out.println(a); }}Copy the code

Final modifier parameter

The keyword final can also be used to modify parameters ina method. A parameter can be declared final ina method’s parameter list, which means there is no way to modify the parameter inside the method. Such as:

Public class FinalPara {public void withFinal(final int a) {system.out.println (a); // a = 9; // compilation error, not allowed to change final parameter value}}Copy the code

There is a withFinal method in this code, and the method’s input parameter, A, is final. Next, we first print out the input parameter a, which is allowed, meaning we can read its value; But let’s assume that changing a in the method, such as a = 9, will result ina compilation error and will not allow you to change the value of the final argument.

The core of this can be summed up in one sentence: once assigned, it cannot be changed

Final decoration method

One of the reasons for choosing final decoration methods is to improve efficiency, because in earlier Versions of Java, final methods were converted to inline calls, which eliminated the overhead of method calls and made the program run more efficiently. In later Versions of Java, however, the JVM will automatically optimize for these optimizations, so we programmers don’t need to use final modifications to make these optimizations, and there is no performance benefit if we do.

Currently, the only reason we use final to modify a method is because we want to lock the method, which means that no inherited class can change the meaning of the method. That is, methods modified by final cannot be overridden or overridden. Let’s take a code example:

Final decorates ordinary methods

/** * Public FinalMethod {public void drink() {} public final void eat() {}} class SubClass Extends FinalMethod {@override public void drink() {// non-final methods are allowed to be overridden} Public final SubClass() {} public final SubClass() {}Copy the code

There are two classes in this code. The first is FinalMethod, which has a drink method and a eat method, where the eat method is modified by final. The second SubClass inherits the previous FinalMethod class.

We then try to Override the drink method, which is of course ok because it is non-final. Override then attempts to Override the eat method, and you will find that overriding the eat method in the following subclasses will not work, and will result ina compilation error because overriding the final method is not allowed.

Another thing to note here is that at the bottom we write a public final SubClass () {}, which is a constructor, which also fails compilation because constructors are not allowed to be modified by final.

Final private method

There is an exception here where final is used to decorate private methods. Let’s take a look at the following example of code that might seem to be out of order:

/** * Private methods implicitly specify final */ public class PrivateFinalMethod {private Final void privateEat() {}} class SubClass2 extends PrivateFinalMethod {private final void privateEat() {// This is not a real rewrite}}Copy the code

In this code example, we first have a PrivateFinalMethod class that has a final modified method, but note that this method is private. Next, The following SubClass2 extends the first PrivateFinalMethod class, that is, inherits the first class; The subclass then writes a private final void privateEat() method, and the compiler passes. That is, the subclass has a method named privateEat, and it is final.

SubClass2 successfully overrides the parent’s privateEat method, since this method appears exactly as it does in the parent class. Does this mean that there is something wrong with our previous statement that methods modified by final cannot be overridden?

The conclusion is still true, but all private methods ina class are implicitly specified to be automatically modified by final, and adding the final keyword to them doesn’t do anything. Since our method is of private type, there is no way for a subclass to get the parent’s method, let alone override it. In the above code example, the subclass does not actually override the parent class’s privateEat method. The two privateEat methods of the subclass and the parent class are independent of each other; they just happen to have the same name.

To prove this, we try to Override the subclass’s privateEat Method. This will say “Method does not Override Method from its superclass.” This method does not override the parent method, which proves that this is not a real rewrite.

Final modifier class

In the case of final modifying a class, the implication of final modifying a class is clear: the class is “not inheritable.” Let’s take a code example

/** * */ public final class FinalClassDemo {//code}// class A extends FinalClassDemo {}// compile error, cannot inherit final classCopy the code

If you try to write class A extends FinalClassDemo to A class that has A final modifier called FinalClassDemo, you will get A compiler error because the syntax says you cannot inherit A final class. So what’s the purpose of adding final to A class?

If we design it this way, it means that not only do we not inherit the class, but we don’t allow others to inherit it, so it can’t have subclasses, which is somewhat thread-safe.

The classic String class, for example, is final, so we never see any classes that inherit from String, which is important to keep String immutable.

One caveat here, however, is that if we add final to a class, this does not automatically mean that its member variables are added final. In fact, there is no interaction between the two. In other words, just because a class is final does not mean that its properties are automatically final. That is, the properties of the objects of this class can be changed

But remember, the final modifier means that the method cannot be overridden, and now if you add final to a class, that class doesn’t even have subclasses, so overriding is unlikely. So, ina final class, all methods, whether public, private, or any other permission modifier, are automatically and implicitly designated as final.

One caveat here is that if we do use final classes or methods, we need to specify why. Why is that? Because the maintainer of the future code, he might not understand why we use final here, because if we use final, he can’t rewrite it, or if we use final, he can’t inherit it.

Therefore, in order to prevent subsequent maintainers from being confused, it is necessary or obligation for us to explain the reason, so that some problems in subsequent maintenance will not occur.

In many cases, we don’t have to rush to declare a class or method final until later in development so that we can better understand how classes interact or how methods relate to each other. So you might find that you don’t need to use final at all, or that you don’t need to scale too much, and that you can refactor the code to apply final to a smaller range of classes or methods with less impact.

Why you can’t have “immutability” when you add final

To answer these questions, we first need to know what is Immutable. An object is “immutable” if its state cannot be changed after it is created.

Let’s take an example, like the following Person class:

public class Person {
    final int id = 1;
    final int age = 18;
}
Copy the code

If we create a Person object, there will be two properties, id and age, and since both are final, once the Person object is created, all of its properties, ID and age, will be immutable. If we want to change the value of the attribute, we will get an error, as shown below:

public class Person { final int id = 1; final int age = 18; public static void main(String[] args) { Person person = new Person(); // person.age=5; // compilation error, cannot modify final variable value}}Copy the code

For example, if we try to change the Person object, such as changing age to 5, the compilation fails, so a Person object like this is immutable, which means its state cannot be changed.

When final decorates an object, only the reference is immutable

It is important to note that when we use final to modify a variable that refers to an object type (as opposed to one of the eight basic data types, such as int), final only serves to ensure that the reference to the variable is immutable, while the contents of the object remain mutable. Let’s explain this.

As we explained in the previous lesson, a variable modified by final means that it cannot be modified once it has been assigned. That is, it can only be assigned once. If we try to assign a variable that has already been modified by final, we will get a compilation error. Let’s use the following code to illustrate:

Public class FinalVarCantChange {private final int finalVar = 0; private final int finalVar = 0; private final Random random = new Random(); Private final int array[] = {1,2,3}; public static void main(String[] args) { FinalVarCantChange finalVarCantChange = new FinalVarCantChange(); // finalVarCantChange.finalVar=9; / / compile error, not allowed to modify the final variables (basic types) / / finalVarCantChange. Random = null; Array = new int[5]; finalVarCantChange.array = new int[5]; // compilation error, final variable (array) not allowed to be modified}}Copy the code

Here we create a variable of type int, a variable of type Random, and an array, all of which are final; Then trying to modify them, such as changing the value of the int variable to 9, or setting the random variable to NULL, or respecifying the contents of the array, would not compile.

This proves that “a variable modified by final means that it cannot be modified once assigned.” This rule is unambiguous for primitively typed variables, but for object types, final only guarantees that the reference to the variable is immutable, while the object itself can still be changed. The same applies to arrays, because arrays are also objects in Java. Let’s take a look at the output of the following Java program:

class Test { public static void main(String args[]) { final int arr[] = {1, 2, 3, 4, 5}; // Note that arr is final for (int I = 0; i < arr.length; i++) { arr[i] = arr[i]*10; System.out.println(arr[i]); }}}Copy the code

This code has a Test class that has only a main method with a final arR array. Note that an array is a type of object and is now final, so it means that the reference to a variable cannot be modified once it has been assigned. But now we want to prove that the contents of the array object can be changed, so we use the for loop to multiply the contents of the array object by 10, and print the result as follows:

10, 20, 30, 40, 50Copy the code

As you can see, it prints 10, 20, 30, 40, 50 instead of the original 1, 2, 3, 4, 5, which proves that even though the array ARR is final and its references cannot be modified, its contents can still be modified.

The same is true for objects that are not arrays. Let’s look at the following example:

class Test { int p = 20; public static void main(String args[]){ final Test t = new Test(); t.p = 30; System.out.println(t.p); }}Copy the code

The Test class has a p attribute of type int. After creating t of Test in main and modifying it with final, we try to change the value of member variable P in it and print the result. The program will print “30”. At the beginning, the value of P was 20, but it changed to 30 after modification, indicating that the modification was successful.

This leads to the conclusion that when final modifies a variable to an object, the content of the object itself can still be changed.

Final and immutable

This raises a question: what is the relationship between final and immutability?

So let’s compare final and immutability. The keyword final ensures that a reference to a variable remains unchanged, but immutability means that an object cannot change its state once it has been created. It emphasizes the content of the object itself, not the reference, so final and immutability are very different.

For an object of a class, you must ensure that all its internal states (including the internal attributes of its member variables, etc.) remain unchanged after it is created. This requires that the states of all member variables are not allowed to change.

There is a saying that “the simplest way to guarantee immutability of an object is to declare all attributes of the class final”. This rule is not entirely true and usually only applies to cases where all attributes of the class are primitive, as in the previous example:

public class Person {
    final int id = 1;
    final int age = 18;
}
Copy the code

The Person class has two properties, final Int ID and final Int age, which are both basic and final, so objects of the Person class are indeed immutable.

However, if a class has a final modified member variable that is not a primitive type but an object type, then the situation is different. With that in mind, we know that if we add final to an attribute of an object type, its internal member variables can still change, because final can only guarantee that its reference remains unchanged, not that its content remains unchanged. So if the content of an object type changes, it means that the entire class is no longer immutable.

So we come to the conclusion that immutability does not mean that the objects of a class are immutable simply by modifying all properties of the class with final.

So there’s a big question, if I have a class that has a member variable of an object type, what do I have to do to make sure that the whole object is immutable that an object of a class that has a member variable of an object type, has an example of immutability.

public class ImmutableDemo { private final Set<String> lessons = new HashSet<>(); Public ImmutableDemo() {lessons. Add (" lesson 01: There is only one way to implement threads." ); Lesson 02: How to stop a thread correctly? Why is stopping the volatile flag bit wrong?" ); Lesson 03: How do threads transition between six states? ); } public boolean isLesson(String name) { return lessons.contains(name); }}Copy the code

In this class there is a final and private modified Set called lessons, which is a HashSet; We then add three values to the HashSet in the constructor, topics in lecture 01, 02, and 03. The class also has a method called isLesson that determines whether the parameters passed in are part of the class title. The isLesson method uses the lessons contained method to determine whether the parameters passed in are part of the class title. The isLesson method returns true if it contains the lessons, and false otherwise. That’s all there is to this class, no additional code.

In this case, even though the Lessons are of type Set, even though they are an object, they are immutable for objects of the ImmutableDemo class.

Since the Lessons object is final and private, the reference does not change and cannot be accessed externally, and the ImmutableDemo class does not have any method to modify the contents contained in the Lessons. We just add an initial value to the Lessons in the constructor, so once the ImmutableDemo object is created, that is, once the constructor is executed, there is no further opportunity to modify the lessons. The ImmutableDemo class, on the other hand, has only one member variable, and that member variable cannot be changed once constructed, so that the ImmutableDemo class object is immutable. This is a nice “class object that contains the member variable of the object type, Immutability “.

Immutability of String

So what’s behind this? Let’s look at some important source code for the String class:

public final class String implements Java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[]; / /... }Copy the code

First, you can see that there is a very important property in there, which is a private final char array called Value. It stores each character in the string, and the array of values is final, meaning that once a value is assigned, the reference cannot be modified; In addition, it can be found in the String source code that there is no other method to modify the contents of the value array except the constructor. Moreover, the permission of value is private, and external classes can not access it, so the value is immutable.

Is it possible that another class inherits the String class and then overrides the related methods to change the value of value? Wouldn’t that make it mutable?

That’s a good question, but don’t worry about it. The String class is final, so the String class is not inherited, so no one can break the immutability of the String class by extending or overwriting it.

That’s why strings are immutable.

conclusion

Final has a very different meaning when applied to a variable, method, or class, so we’ll take a look at each of the three cases: modifying a variable means that once assigned, it cannot be modified; A modifier means that it cannot be overridden; Modifying a class means it cannot be inherited.

When explaining the final variable modification, we also expanded the analysis of the three different cases of member variables, static variables and local variables respectively. It can be seen that their assignment timing is also different. If we use blank final, we can make variables more flexible. There is also a special case where final modifies the parameter, meaning that it is not allowed to change the content of the parameter.