This is the 13th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

First, final basic use

1.1. Modification Classes

When a class is defined as final, that class cannot have subclasses, all methods ina final class are implicitly final and cannot be overridden, so it makes no sense to add a keyword to any method ina final class.

How are classes of final type extended? The two most important relationships in design patterns are inheritance/implementation; The other is a combinatorial relationship. So when you encounter classes that cannot be inherited (final modified classes), consider composition.Copy the code

1.2. Modification methods
  • The private method is implicitly final
  • Final methods can be overridden

Private Final: A class in which all private methods are implicitly specified as final cannot override them because they are not available. There is no benefit in adding the final keyword to a private method.

Final methods can be overridden: Final methods of a parent class cannot be overridden by subclasses, so final methods can be overridden? Can the

1.3. Modify parameters

Java allows parameters to be specified as final declaratively in parameter lists, which means that the object to which a parameter reference refers cannot be changed ina method. This feature is primarily used to pass data to anonymous inner classes.

1.4. Modify variables

Are all final modified fields compile-time constants?

Public class Test {// final int I = 1; final static int J = 1; Final int[] a = {1,2,3,4}; // non-compile-time constant Random r = new Random(); final int k = r.nextInt(); public static void main(String[] args) { } }Copy the code

The value of k is determined by the random number object, so not all final modified fields are compile-time constants, but the value of k cannot be changed after initialization.

1.5, the static final

A field that is both static and final occupies only an immutable chunk of storage and must be assigned at definition time or the compiler will reject it.

import java.util.Random; public class Test { static Random r = new Random(); final int k = r.nextInt(10); static final int k2 = r.nextInt(10); public static void main(String[] args) { Test t1 = new Test(); System.out.println("k="+t1.k+" k2="+t1.k2); Test t2 = new Test(); System.out.println("k="+t2.k+" k2="+t2.k2); }}Copy the code

Execution result output:

k=2 k2=7
k=8 k2=7
Copy the code

The static keyword is used to modify fields that do not belong to an object, but belong to the class. Static final fields occupy only a portion of memory and are not changed once they are initialized.

Reordering rules for final domains

2.1 Final fields are basic types
public class FinalDemo { private int a; Private final int b; // Final domain private static FinalDemo FinalDemo; public FinalDemo() { a = 1; // 1. Write common field b = 2; } public static void writer() {finalDemo = new finalDemo (); } public static void reader() { FinalDemo demo = finalDemo; Int a = demo.a; Int b = demo.b; //5. Read final fields}}Copy the code

Suppose thread A is executing writer() and thread B is executing reader().

Write final field reorder:

The reorder rules for writing final fields prohibit the reorder of writes to final fields outside the constructor. The implementation of this rule includes two aspects: 1. JMM prohibits the compiler from reordering writes to final fields outside the constructor. 2. The compiler inserts a StoreStore barrier after the final field is written but before the constructor return. This barrier prevents the processor from reordering writes to final fields outside the constructor.

Analyze writer method: 1. Construct a FinalDemo object; 2. Assign this object to the member variable finalDemo.

image.png

Since there are no data dependencies between A and B, the common field (common variable) A might be reordered outside the constructor, and thread B might read the value (0) of the common variable before it was initialized, which could lead to an error. The final field variable B, according to the reordering rules, prevents the final modified variable B from being reordered outside the constructor, so that B can be properly assigned and thread B can read the initialized value of the final variable. Therefore, reordering rules that write final fields ensure that an object’s final field is properly initialized before its reference is visible to any thread, a guarantee that normal fields do not have.

Read final domain reorder rules:

The reordering rules for reading final fields are: In a thread, the JMM forbids the reordering of the first read object reference and the first read object containing the final field. The processor inserts a LoadLoad barrier before the read final field operation. In fact, there is an indirect dependency between the read object reference and the read final field. Normally the processor does not reorder these two operations. But some processors will reorder. Therefore, this reordering prohibition is specifically for these processors.

The read() method contains three operations: 1. First read the reference variable finalDemo; 2. Read the common field A of the reference variable finalDemo for the first time; 3. Read final field B of reference variable finalDemo for the first time;

image.png

The normal field of the read object is reordered before the reference of the read object. Thread B is reading the normal field variable of the read object before it has read the reference of the read object. This is avoided by final field reads that qualify a reference to the object before reading a final field variable.

Reorder rules for reading final fields ensure that the reference to the object containing the final field of an object is read before reading the final field of the object.

2.2 Final fields are reference types

Write to a member field of a final modified object:

According to reference data types, writing for the compiler and final domain in highly increased such constraints: in the constructor of a final modified objects to members of the domain, and then in the constructor to this is constructed object reference is assigned to a reference variable, the two operations cannot be reorder.

public class FinalReferenceDemo {
    final int[] arrays;
    private FinalReferenceDemo finalReferenceDemo;

    public FinalReferenceDemo() {
        arrays = new int[1];  //1
        arrays[0] = 1;        //2
    }

    public void writerOne() {
        finalReferenceDemo = new FinalReferenceDemo(); //3
    }

    public void writerTwo() {
        arrays[0] = 2;  //4
    }

    public void reader() {
        if (finalReferenceDemo != null) {  //5
            int temp = finalReferenceDemo.arrays[0];  //6
        }
    }
}
Copy the code

If thread A executes writerOne, then thread B executes writerTwo, and then thread C executes reader

image.png

Since writes to final fields prohibit reordering out of the constructor, 1 and 3 cannot be reordered. Since a member field write to a reference object ina final field cannot be reordered with the subsequent assignment of the constructed object to a reference variable, 2 and 3 cannot be reordered

Member field read operations on objects that are final modified

The JMM ensures that thread C will at least see writerThread A’s writes to the member fields of final referenced objects (i.e., arrays[0] = 1), whereas writer thread B’s writes to array elements may or may not be seen. The JMM does not guarantee that thread C, the writer of thread B, is visible. There is a data race between thread B and thread C, and the result is unpredictable. If visible, use locking or volatile.