The word final literally means final and cannot be modified. This seems like a syntax that you can probably tell by the name, but have you ever looked into how final is used in various scenarios, what to look for, and the Java design ideas behind it?

 

I. Final modifiers

1. Basic: Final modifies variables of basic and reference data types.

  • We all have the basic knowledge that variables modified by final cannot be changed. But the phrase “cannot be changed” has different meanings for different data types.
  • When final modifies data of a basic data type, the value of that data cannot be changed after initialization. When final decorates data of a reference type, i.e., an object, the reference will always point to a memory address after initialization and cannot be modified. However, the object information stored in the memory address can be modified.
  • The previous paragraph may have been abstract, but hopefully the following diagram will help you understand. You will see that although there are different meanings, the essence is the same.
  • The first is the memory diagram of final decorating the basic data types

  • As shown in the figure above, the variable A will always point to chunk 003 after initialization, and this chunk of memory will always hold the value 100 after initialization.
  • Below is a diagram of the final decorated reference data type

  • In the figure above, the variable p refers to memory 0003, which holds a handle to the object p. The value of this handle cannot be modified, meaning that the variable p always refers to the object p. But the data of the P object can be modified.
// Example code
public static void main(String[] args) {
    final Person p = new Person(20."Charcoal roasted oysters");
    p.setAge(18);   // You can modify the data of the p object
    System.out.println(p.getAge()); / / output 18

    Person pp = new Person(30."Charcoal burning with oysters");
    p = pp; // This line of code will report an error and will not compile because p is final and always points to the p object defined above, not to the PP object.
}
Copy the code
  • It is easy to see the nature of final modifiers: final modifiers refer to a fixed chunk of memory whose value cannot be changed.
  • The object to which a variable of a reference type points can be modified because a reference variable does not point directly to the data of the object, but rather to a reference to the object. So a variable of a reference type that is final will always point to a fixed object and cannot be modified; The data value of the object can be modified.

 

2. Advanced: Final modifiers are placed in the constant pool during compilation

  • Final is used to define constants. The advantage of defining constants is that you do not need to create the same variable repeatedly. While constant pooling is an important Java technology, variables decorated by the final are put into the constant pool of the calling class during compilation.
  • Take a look at the demo code below. This example is designed for demonstration purposes and hopefully makes it easier for you to understand the point.
public static void main(String[] args) {
    int n1 = 2019;          // Common variables
    final int n2 = 2019;    // Final modifier variable

    String s = "20190522";  
    String s1 = n1 + "0522";	// Concatenation string "20190512"
    String s2 = n2 + "0522";	

    System.out.println(s == s1);	//false
    System.out.println(s == s2);	//true
}
Copy the code

First of all, the integer -127-128 is loaded into the constant pool by default, which means that if -127-128 is involved in the integer operation, the default value of the integer is determined at compile time. So HERE I purposely chose the number 2019(greater than 128) to avoid having numbers in the constant pool by default.

  • The above code works like this:
  • First, according to the principle that finally-decorated constants are placed in the constant pool at compile time, n2 is placed in the constant pool at compile time.
  • The string “20190522” corresponding to the s variable is then put into the string constant pool, and a reference is supplied back to the s variable.
  • At this time, the string s1 is concatenated. Since the data corresponding to N1 is not put into the constant pool, S1 cannot be concatenated temporarily. The corresponding value of S1 can only be determined when the program is loaded and run.
  • However, when splicing S2, since N2 already exists in the constant pool, it can be spliced directly with “0522”, and the result is “20190522”. At this point, the system will check the string constant pool and find that the string 20190522 already exists, so it directly returns the reference to 20190522. So s2 and s refer to the same reference, which refers to 20190522 in the string constant pool.

 

  • When the program executes, the n1 variable is referred to.
  • When s1 is concatenated, a new String object is created, which means that 20190522 in the String constant pool will provide a new reference.
  • So s1 and s with “==” return false because of the different reference. S2 and s point to the same reference and return true.

Summary: The point of this example is that, since final modifiers enter the constant pool at compile time, any operation involving the constant is likely to have been completed at compile time.

 

3. Explore: Why do local/anonymous inner classes only use variables that are final modified when using external local variables?

Tip: After JDK1.8, when accessing external local variables through inner classes, there is no need to explicitly declare external local variables as final. Not that we don’t need to declare it final, but the system does it for us at compile time. However, it is important to understand why we use final to decorate external local variables.

public class Outter {
    public static void main(String[] args) {
        final int a = 10;
        new Thread(){
            @Override
            public void run(a) { System.out.println(a); } }.start(); }}Copy the code
  • The above code will not compile without adding the final keyword to the external local variable A. Try this: When the main method has executed, the stack frame of the main method will pop up. If the Thread object hasn’t finished its life cycle and hasn’t executed the print statement, it won’t be able to access the external variable A.
  • So why does adding the final keyword compile properly? Let’s see how an inner class calls an external member variable by looking at the decompile code.
  • We can compile a.class file with javac (or IDE) and type it on the command lineThe absolute path to the javap-c. Class file, you can view the decompiled code for the.class file. The Outter class above is compiled to produce two.class files, which areOutter. The class and Outter $1. ClassThat is, the inner class is compiled separately into a.class file. Given belowOutter$1.classDecompiled code for.
Compiled from "Outter.java"
final class forTest.Outter$1 extends java.lang.Thread {
  forTest.Outter$1();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Thread."<init>":()V
       4: return

  public void run();
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: bipush        10
       5: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
       8: return
}
Copy the code
  • Locate therun()Method decompile line 3 in the code:
  • 3: bipush 10
  • We see that the value of a is of the inner classrun()Method execution is stored in the local variable table in the form of a stack, that is, when the internal class prints the value of the variable a, the variable a is not the external local variable a, as it would be if it wereloadThe instruction loads the value of a variable. That is, the system makes a copy of the external local variable A into the inner class. The inner class has a variable that points to the value referred to by the external variable A.

 

  • It is possible to make a copy of a variable without final, but the value of the variable is not known at compile time. It’s time to consider a new problem: now that we know that the inner class variable a and the outer local variable A are two completely different variables, if the inner class changes the value referred to by the variable A during the run() method, we will have a data inconsistency problem.
  • Because the inner class and the outer class access the same variable a, we should use the final modifier when using the outer local variable in the inner class. In this way, the value of the local variable a will never change and data inconsistency will be avoided.

 

2. Final modification method

  • Using a final modifier serves two purposes. The first is to lock the method and not allow any inherited class to modify it.
  • Another benefit is inlining methods in the compiler to improve efficiency. However, this is rarely used today, and recent Java versions have done a good job of optimizing this part. But in order to satisfy the curiosity or understand what is the method of introversion.
  • Method restraint: When a method is called, the system needs to save field information, set up stack frames, restore threads, etc., all of which are relatively time-consuming. If you decorate a method A with final, the code for method A is embedded directly in the code block calling a when other classes that call method A compile.
/ / the original code
public static void test(a){
    String s1 = "Method A of wrapping";
    a();
    String s2 = "Method A of wrapping";
}

public static final void a(a){
    System.out.println("I am the code in method A");
    System.out.println("I am the code in method A");
}

// After compilation
public static void test(a){
    String s1 = "Method A of wrapping";
    System.out.println("I am the code in method A");
    System.out.println("I am the code in method A");
    String s2 = "Method A of wrapping";
}
Copy the code
  • When the method is very large, there is little performance gain from such an embedding approach, and in recent Java releases, there is no need to use final methods for these optimizations.

 

Iii. Final modifier class

  • The purpose of using final modifiers is simple: to indicate that the class cannot be inherited.
  • You can use the final keyword when a program has a class that will never be inherited
  • All member methods of a class that is final will be implicitly final.

   

  • The resources
  • www.cnblogs.com/ChenLLang/p…
  • www.cnblogs.com/xrq730/p/48…
  • Gitbook. Cn/books / 5 c6e1…
  • www.cnblogs.com/xrq730/p/48…
  • www.cnblogs.com/dolphin0520…
  • www.cnblogs.com/dolphin0520…