In depth analysis of the use of the final keyword in Java programming

  • 2020-04-01 04:33:43
  • OfStack

When properties, methods, and classes are declared in Java, they can be decorated with the keyword final. The final variable is a constant and can only be assigned once; Final methods cannot be subclassed; Final classes cannot be inherited.
1. Final members
Declaring final fields helps the optimizer make better optimization decisions, because if the compiler knows that the field's value will not change, it can safely cache the value in the register. Final fields also provide an additional level of security by having the compiler force the field to be read-only.
 
1.1 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) final members must be initialized at declaration time (when final variables are defined) or in the constructor, but not elsewhere.
Example 1 Bat. Java


public class Bat {
  final double PI = 3.14; //Assign a value at definition time
  final int i; //Because you're initializing in the constructor, you can't assign any more values here
  final List<Bat> list; //Because you're initializing in the constructor, you can't assign any more values here
 
  Bat() {
    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());
    // b.i=25;
    // b.list=new ArrayList<Bat>();
    System.out.println("I=" + b.i + " List Type:" + b.list.getClass());
    b = new Bat(23, new ArrayList<Bat>());
    b.list.add(new Bat());
    System.out.println("I=" + b.i + " List Type:" + b.list.getClass());
  }
}

 
Results:


I=100 List Type:class java.util.LinkedList
I=23 List Type:class java.util.ArrayList

 
There are two lines in the main method that are commented out, and if you remove the comment, the program will not compile, which means that neither the value of I nor the type of list, once initialized, really cannot be changed. However, b can specify the value of I or the type of list by reinitialization.
 
1.2 invalid initialization of final reference fields
The proper use of final fields can be tricky, especially for object references whose constructors can throw exceptions. Because final fields must be initialized only once in each constructor, if the constructor referenced by the final object might throw an exception, the compiler might report that the field was not initialized. Compilers are generally smart enough to find two mutually exclusive branches of code (e.g., if... The initialization in each branch of the else block happens only once, but it works on the try... The catch block is usually not so "forgiving."
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 it like 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 use of final members
When you define a variable ina class, and you prefix it with the final keyword, that means that the variable is immutable once it's initialized, which means that the value is immutable for primitive types, and the reference is immutable for object variables. However, objects themselves can be modified, and Java does not provide a way to keep any object constant. This restriction also applies to arrays, which are also 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 without significant difference. VAL_THREE is a more typical way to define a constant: public, which can be used outside of a package; Static to emphasize that there is only one; It's defined as final to say that it's a constant.
The variables of the final tag become constants, but this "constant" can only be used within the class, not directly outside the class. But when we mark a constant with public static final, the constant becomes a global constant (a field that is both static and final occupies only a block of unchangeable storage space). And constants defined in this way can only be assigned at definition time, not 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);
  }
}

 
The results of


fd1:i4:6, i5=3
Creating new FinalData
fd1:i4:6, i5=3
fd2:i4:17, i5=3

The example section shows the difference between defining final values as static (i5) and non-static (i4). This difference is only apparent when the value is initialized during the runtime because the compiler treats compile-time values the same. (and they may disappear as a result of optimization.) You'll see the 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 and is initialized at load time, rather than 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(), just because v2 is final doesn't mean you can't change its value. Since it's 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 (you can change its value, but you can't point to a new object), an array is another reference.
 
1.4 addresses the limitations of final arrays
Although an array reference can be declared final, the elements of that array cannot. This means that the classes that expose public final array fields or that return references to them through their methods are not immutable.


// Not immutable -- the states array could be modified by a malicious
// callerpublic
class DangerousStates {
  private final String[] states = new String[] { "Alabama", "Alaska", "ect" };
 
  public String[] getStates() {
    return states;
  }
}

 
Also, although an object reference can be declared as a final field, the object it references can still be mutable. If you want to use final fields to create immutable objects, you must prevent references to arrays or mutable objects from "escaping" from your class. A simple way to do this without cloning the array repeatedly is to turn the array into a List.


// Immutable -- returns an unmodifiable List insteadpublic
class 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 use of final parameters
Another use is to define the method of parameters for the final, the basic types of variables, do no practical significance, because the basic types of variables in the calling method is by value, that is to say, you can change the parameter variable in a method and will not affect the call statement, for object variables, however, seem to be very practical, because the object variable is passed its reference when passing, you amend in the method of the object variables will also affect the call object of the variables in the statement, when you do not need to change in the method as a parameter object variables, clear use their final statement, This will prevent you from inadvertently modifying the calling method.
 
1.6 about parameter variables in inner classes
In addition, when inner classes in methods use variables in methods, the parameter variables must be declared as final to 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.1final method usage
1) to ensure that the behavior of a function remains unchanged during inheritance and cannot be overridden (overridding), final methods can be used.
 
2) all private and static methods in class are naturally final.
 
2.2 final versus private keyword
All private methods in the class are implicitly specified as final. Since you cannot access the private method, you cannot override it.
"Override" occurs only if a method is part of the interface of the base class. That is, you must be able to transform an object up to its basic type and call the same methods. If a method is private, it is not part of the interface of the base class. It's just some code hidden in a class with the same name. But if you generate a public, protected, or package access method in the same way in the exported class, the method does not produce the "same name only" case that occurs in the base class. At this point, you don't override the method, you just generate a new method. Since the private method is untouchable and effectively hidden, nothing needs to be considered except that it exists because of the organizational structure of the class to which it belongs.
3. The final class
When the whole of a class is defined as final, it cannot be inherited. And because final classes prohibit inheritance, all methods in final classes are implicitly specified as final because they cannot be overridden.
Final is used with classes or methods to prevent links between methods from breaking. For example, suppose that an implementation of a method of class X assumes that method M will work in some way. Declaring X or M final will prevent derived classes from redefining M in this way, causing X to work incorrectly. While it may be better to implement X without these internal dependencies, it is not always feasible, and using final prevents such incompatible changes in the future.

PS: differences between final,finally and finallize

Final is used to declare properties, methods, and classes, to indicate that properties are immutable, methods cannot be overridden, and classes cannot be inherited. Finally is the part of the exception-handling statement structure that always executes. Finallize is a method of the object class that is called when it is executed in the garbage collection mechanism.

Related articles: