This is the 19th day of my participation in the August More Text Challenge

Phase to recommend

  • Java Basics
  • Java concurrent programming

First, final basic use

1. Modifier class

When the whole of a class is defined as final, it indicates that you cannot intend to inherit the class, and that no one else is allowed to do so. That is, this class cannot have subclasses.

Note: All methods ina final class are implicitly final, and since they cannot be overridden, it makes no sense to add the final keyword to any method ina final class.

  • withfinalTo modify a class means that the class cannot be inherited;
  • finalMember methods ina class are implicitly specified as final methods;
  • befinalModified class,finalMember variables in a class can be designed to befinal.

When designing a class, we should consider whether the class will be inherited. If it can be inherited, the class should not use the final modifier. In general, utility classes are designed to be final. In the JDK, classes that are designed to be final include String, System, and so on.

The two most important relationships in design patterns are inheritance/implementation; The other is a combinatorial relationship. So consider combinatorial relationships when dealing with classes that cannot be inherited (final modified)

/ * * *@akaing* /
class MyString{

    private String innerString;

    / /... init & other methods

    // Support the old method
    public int length(){
        return innerString.length(); // Call the old method with innerString
    }

    // Add a new method
    public String toMyString(){
        / /...}}Copy the code

2. Modification methods

Note:

  • Methods decorated with final cannot be overridden.
  • Of a classprivateThe method is implicitly specified asfinalMethods.
  • If the parent class has a final modifier, the subclass cannot override it.

2.1 private final

All private methods in the class are implicitly specified as final and cannot be overridden because they are not available.

public class Base {
    private void test() {
    }
}

public class Son extends Base{
    public void test() {
    }
    public static void main(String[] args) {
        Son son = new Son();
        Base father = son;
        //father.test();}}Copy the code

Both Base and Son have methods called test(), but this is not an override. Methods that are modified by private are implicitly final and cannot be inherited, let alone overridden. The test() method in Son is just a new member of Son. Son goes up to get father, but father.test() is not executable because the test method in Base is private and cannot be accessed.

2.2 Final methods can be overridden

public class FinalExampleParent {
    public final void test() {
    }

    public final void test(String str){}}Copy the code

3. Modify member variables

Note:

  • Values must be initialized.
  • befinalModifier member variables can be assigned in two ways: 1, directly; 2, all initial values are assigned in the constructor.
  • If the decorated member variable is of a primitive type, the value of the variable cannot be changed.
  • If the modifier is a reference type, the address of the reference cannot be changed, but the contents of the object to which the reference refers can be changed.
public class Test {    
    final int age = 18;
    final String name;    
    final String[] hobby;    
    public Test() {        
        this.name = "lvmenglou";        // Use it correctly
        //this.age = 20; // Error
        this.hobby = new String[4];     // Use it correctly
        this.hobby[0] = "movie";          // Use it correctly
        this.hobby[1] = "sing song";    // Use it correctly}}Copy the code

4. Modify parameters

4.1 Are all final modified fields compile-time constants?

public class Test {
    // Compile-time constants
    final int i = 1;
    final static int J = 1;
    final int[] a = {1.2.3.4};
    // Non-compile-time constants
    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.

4.2 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); }} Output result: k=2 k2=7
k=8 k2=7
Copy the code
  • The value of k varies from object to object, but the value of k2 is the same
  • It can be simply understood asstatic finalThe modified field occupies only one portion of memory and is not changed once initialized.

4.3 blank final

Java allows blank final, that is, fields that are declared final but have no fixed value, but must be assigned before the field is used, which gives us two options:

  • Assign at the definition(This is not called blank final)
  • Assignment in the constructor ensures that the value is assigned before it is used.

This enhances the flexibility of the final.

public class Test {
    final int i1 = 1;
    final int i2;/ / blank final
    public Test() {
        i2 = 1;
    }
    public Test(int x) {
        this.i2 = x; }}Copy the code

You can see that i2 is more flexible in its assignment. Note, however, that if the field is static and final, it can only be assigned at the definition, because the field does not belong to the object, it belongs to the class.

Reordering rules for final domains

1. Final fields are basic types

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

public class FinalDemo {
    private int a;  / / common domain
    private final int b; / / final domain
    private static FinalDemo finalDemo;

    public FinalDemo() {
        a = 1; // 1. Write common fields
        b = 2; // 2. Write final fields
    }

    public static void writer() {
        finalDemo = new FinalDemo();
    }

    public static void reader() {
        FinalDemo demo = finalDemo; // 3. Read object references
        int a = demo.a;    //4. Read common fields
        int b = demo.b;    / / 5. Read the final domain}}Copy the code

1.1 Writing reordering rules for final Domains

The reorder rule for writing final fields forbids reordering of writes to final fields outside the constructor. The implementation of this rule has two main aspects:

  • The JMM disallows the compiler toFinal domainWrite reorder out of the constructor;
  • The compiler will be inFinal domainAfter writing, the constructorreturnBefore that, insert onestorestoreBarrier. This barrier can disable the processorFinal domainThe write is reordered outside the constructor.

The Writer method does two things, although it’s only one line of code:

  • Construct a FinalDemo object;
  • Assign this object to the member variable finalDemo.

Thread execution sequence diagram:

Since there are no data dependencies between a and B, the common field (the common variable) A might be reordered outside the constructor, and thread B might read the value of the common variable before it was initialized (zero), 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.

Reordering rules for writing final fields ensure that:

  • Before the object reference is visible to any thread, the object’sFinal domainHas been correctly initialized, whereas normal fields do not have this guarantee.

1.2 Reading Reordering Rules for final Domains

The reordering rules for reading final fields are:

  • In a thread, the first read object reference and the first read contained in the objectFinal domain, the JMM disallows reordering of these two operations. (Note that this rule applies only to processors.) The processor readsFinal domainInsert one before the operationLoadLoadBarrier. In fact, read a reference to an object and read that object’sFinal domainThere are indirect dependencies, and typically the processor does not reorder these two operations. However, some processors reorder, so this disallow reorder rule is for those processors.

The read() method consists of three operations:

  • First read the reference variable finalDemo;
  • Read the common field A of the reference variable finalDemo for the first time;
  • First read the reference variable finalDemo final and B;

Assuming that thread A’s write process is not reordered, thread A and thread B have A possible execution sequence as follows:

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 the reference of the read object. This is obviously the wrong operation. A final field read avoids this by “qualifying” that a reference to the object has been read before a final field variable is read.

Reading the reordering rules for final fields ensures that:

  • Read from an objectFinal domainBefore you do that, you have to read this and include thisFinal domainObject reference to.

2. Final fields are reference types

2.1 Write to the 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. Note that the word “increment” means that the previous reorder for the final base data type is still used here.

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

Thread A executes the wirterOne method, then thread B executes the writerTwo method, and then thread C executes the Reader method.

Execution sequence diagram:

As a result ofFinal domainWrites to disallow reordering out of the constructor, so 1 and 3 cannot be reordered. Due to aFinal domainWrites to the member field of the reference object cannot be reordered with subsequent assignments of the constructed object to the reference variable, so 2 and 3 cannot be reordered.

2.2 Member field read operations on final modified objects

  • The JMM ensures that thread C at least sees the writer thread A pairfinalReference to the object of the member domain of the write, namely can look downarrays[0] = 1, while writer thread B’s writes to array elements may or may not be seen.
  • The JMM does not guarantee that writes from thread B are visible to thread C, and there is a data race between thread B and thread C, where the outcome is unpredictable. If visible, use locking or volatile.

3. Final summary of reordering

  • Basic data types:

    • Final domain to write: Disallows final field writes and constructor reordering, that is, disallows final field writes to be reordered outside of the constructor to ensure that all final fields of an object are initialized before the object is visible to all threads.
    • Final domain to read: disallows references to first read objects and reordering of final fields that read the object.
  • Reference data types:

    • Additional constraint: disallows writing to the member field of a final-modified object ina constructor and reordering the subsequent assignment of a reference to the constructed object to the reference variable.

Iii. Final Further understanding

1. Why final references cannot “overflow” from constructors

In constructors, the constructed object cannot be made visible to other threads, that is, the object reference cannot “overflow” in the constructor.

public class FinalReferenceEscapeDemo {
    private final int a;
    private FinalReferenceEscapeDemo referenceDemo;

    public FinalReferenceEscapeDemo() {
        a = 1;  / / 1
        referenceDemo = this; / / 2
    }

    public void writer() {
        new FinalReferenceEscapeDemo();
    }

    public void reader() {
        if(referenceDemo ! =null) {  / / 3
            int temp = referenceDemo.a; / / 4}}}Copy the code

Execution sequence diagram:

  • Suppose A thread A executeswriterMethod is executed by another threadreaderMethods. Since there is no data dependency between operations 1 and 2 in the constructor, 1 and 2 can be reordered, and 2 is executed first, at which point the object is referencedreferenceDemoIs an incomplete initialized object, and when thread B tries to read the object, an error occurs.
  • Although still satisfiedFinal domainWrite reorder: when a reference object is visible to all threadsFinal domainThe initialization is complete. However, reference objectsthisThe code still has thread-safety issues.

2. Limitations and limitations of using final

  • When a final member is declared, its value must be set before the constructor exits.

  • Declaring a member pointing to an object as final only makes the reference immutable, not the object to which it refers.

  • If an object is going to be accessed in multiple threads and you don’t declare its members final, you must provide some other way to keep it thread-safe.

  • “Other methods” can include declaring members as volatile and controlling access to all members using synchronized or explicit locking.

Refer to the article

  • The Art of Concurrent Programming in Java
  • Crazy Java Handouts
  • Understanding the Java Memory Model in Depth
  • Java Concurrent Programming