Examples of final keyword usage in JAVA

  • 2020-04-01 04:28:15
  • OfStack

This article illustrates the use of the final keyword in JAVA. Share with you for your reference, as follows:

Depending on the context, the Java keyword final is also slightly different, but often means "it can't be changed." There are two reasons for not wanting to change: one is efficiency, and the other is design. Because the two reasons are so far apart, the key sub final can be misused.

Let's move on to the three USES of final: data, methods, and classes

The final data

Many programming languages have some way of telling the compiler that a block of data is constant. Sometimes it is useful to keep data constant, for example:

A constant that is constant at compile time
2. One is initialized at run time, and you don't want it to be changed.

In this case of compile-time constants, the compiler can substitute the value of the constant into any formula that might use it, that is, the formula can be executed at compile time, which takes some of the burden off the runtime. In Java, such constants must be primitive and final. When defining this constant, you must assign it.

A domain that is both static and final takes up only a block of unchangeable storage space.

When final is applied to an object reference, rather than a primitive type, the implications are somewhat confusing. What cannot be changed with final for a primitive type is its value. With an object reference, it is the reference that cannot be changed, and the object itself can be modified. Once a final reference is initialized to an object, the reference cannot be directed to another object. Java does not provide constant support for any object. This restriction also applies to arrays in general, which are also objects. Such as:


package finalPackage;
import java.util.*;
class Value {
  int i;
  public Value(int i) {
    this.i = i;
  }
}

public class FinalData {
  private static Random rand = new Random(47);
  private String id;
  public FinalData(String id) {
    this.id = id;
  }
  //Compile-time constants Can be compile-time constants:
  private final int valueOne = 9;
  private static final int VALUE_TWO = 99;
  //Typical public constant:
  public static final int VALUE_THREE = 39;
  //Run-time constants Cannot be compile-time constants:
  private final int i4 = rand.nextInt(20);
  static final int INT_5 = rand.nextInt(20);
  private Value v1 = new Value(11);
  private final Value v2 = new Value(22);
  private static final Value VAL_3 = new Value(33);
  //An array of Arrays:
  private final int[] a = { 1, 2, 3, 4, 5, 6 };
  public String toString() {
    return id + ": " + "i4 = " + i4 + ", INT_5 = " + INT_5;
  }
  public static void main(String[] args) {
    FinalData fd1 = new FinalData("fd1");
    // ! fd1.valueOne++; // Error: can't change value
    fd1.v2.i++; // Object isn't constant!
    fd1.v1 = new Value(9); // OK -- not final
    for (int i = 0; i < fd1.a.length; i++)
      fd1.a[i]++; // Object isn't constant!
    // ! fd1.v2 = new Value(0); // Error: Can't
    // ! fd1.VAL_3 = new Value(1); // change reference
    // ! fd1.a = new int[3];
    System.out.println(fd1);
    System.out.println("Creating new FinalData");
    FinalData fd2 = new FinalData("fd2");
    System.out.println(fd1);
    System.out.println(fd2);
  }
  
}

Since valueOne and VALUE_TWO are both final primitive types with compile-time values, both can be used as compile-time constants without significant difference. VALUE_THREE is a more typical way of defining constants: public, which can be accessed by anyone; Static, which means there is only one; It's defined as final, which means it's a constant. Note that the final static primitive type with a constant initial value is named in uppercase and separated by an underscore.

Just because some data is final doesn't mean we know its value at compile time. Using random Numbers to initialize the values of i4 and INT_5 at run time illustrates this. The values of i4 in fd1 and fd2 are unique in the instance and are initialized to 15,13 each time. The value of INT_5 cannot be changed by creating a second FinalData object. This is because it is static and is initialized when the class is loaded (that is, when the class object is first created), rather than every time it is created.

Java might generate "blank final," a field that is declared final but has no initial value. In any case, the compiler ensures that final fields are initialized before use. But whitespace final provides a great deal of flexibility in the use of final, so that a final field can be different depending on some objects, yet remain constant. The following example illustrates one point:


package finalPackage;
class Poppet {
  private int i;
  Poppet(int ii) {
    i = ii;
  }
  public int getI() {
    return i;
  }
  public void setI(int i) {
    this.i = i;
  }
}

public class BlankFinal {
  private final int i = 0; // Initialized final
  private final int j; // Blank final
  private final Poppet p; // Blank final reference
  // Blank finals MUST be initialized in the constructor:
  public BlankFinal() {
    j = 1; // Initialize blank final
    p = new Poppet(1); // Initialize blank final reference
  }
  public BlankFinal(int x) {
    j = x; // Initialize blank final
    p = new Poppet(x); // Initialize blank final reference
  }
  public static void main(String[] args) {
    BlankFinal b1=new BlankFinal();
    BlankFinal b2=new BlankFinal(47);
    System.out.println("b1.j="+b1.j+"tt b1.p.i="+b1.p.getI());
    System.out.println("b2.j="+b2.j+"tt b2.p.i="+b2.p.getI());
  }
  
} 

The final parameter

In Java, parameters ina parameter list may be declared as final. This means that you do not change the object to which the argument points. Such as:


package finalPackage;
class Gizmo {
  public void spin(String temp) {
    System.out.println(temp+" Method call Gizmo.spin()");
  }
}

public class FinalArguments {
  void with(final Gizmo g) {
    // ! g = new Gizmo(); // Illegal -- g is final
  }
  void without(Gizmo g) {
    g = new Gizmo(); // OK -- g not final
    g.spin("without");
  }
  // void f(final int i) { i++; } // Can't change
  // You can only read from a final primitive:
  int g(final int i) {
    return i + 1;
  }
  public static void main(String[] args) {
    FinalArguments bf = new FinalArguments();
    bf.without(null);
    bf.with(null);
    System.out.println("bf.g(10)="+bf.g(10));
  }
  
} 

There are two reasons to use the final method. The first reason is to lock the method to prevent any class that inherits it from modifying its meaning. This is by design: you want to ensure that the methods used in inheritance remain the same and are not overwritten.

The second reason the final method was recommended in the past was efficiency. In early implementations of Java, if you specified a method as final, you agreed that the compiler would make all calls to that method inline. When the compiler found a final method call command, it will according to your own careful judgment, skip the inserted code this way of normal calls and execution method invocation mechanism (press parameters into the stack, jumped method executes the code, and then jump back to clean up the stack of parameters, process the return value), and with the copy of the actual code in the method body method call. This will eliminate the overhead of method invocation. Of course, if a method is large, your code will inflate, and you may not see any performance gains from embedding because the performance gains will be reduced by the amount of time spent in the method.

In recent versions of Java, virtual machines (especially hotspot technologies) can detect these situations and optimize to get rid of those extra embedded calls that are less efficient, so you no longer need to optimize using final methods. In fact, the practice is gradually being discouraged. When using Java se5/6, you should leave the compiler and JVM to deal with efficiency issues, and only set the method to final if you want to explicitly prohibit overwriting.

Final and private keywords

All private methods ina class are implicitly specified as final. Because you can't access the private method you can't override it. You can add the final modifier to the private method, but that makes no sense. Such as:


package finalPackage;

class WithFinals {
  // Identical to "private" alone:
  private final void f() {
    System.out.println("WithFinals.f()");
  }
  // Also automatically "final":
  private void g() {
    System.out.println("WithFinals.g()");
  }
}
class OverridingPrivate extends WithFinals {
  private final void f() {
    System.out.println("OverridingPrivate.f()");
  }
  private void g() {
    System.out.println("OverridingPrivate.g()");
  }
}
class OverridingPrivate2 extends OverridingPrivate {
  public final void f() {
    System.out.println("OverridingPrivate2.f()");
  }
  public void g() {
    System.out.println("OverridingPrivate2.g()");
  }
}
public class OverideFinal {
  public static void main(String[] args) {
    WithFinals w1 = new WithFinals();
    // ! w1.f(); //Error , cannot access private methods 
    // ! w1.g(); //Error , cannot access private methods 
    OverridingPrivate w2 = new OverridingPrivate();
    // ! w2.f(); //Error , cannot access private methods 
    // ! w2.g(); //Error , cannot access private methods 
    OverridingPrivate2 w3 = new OverridingPrivate2();
    w3.f();
    w3.g();
  }
  
}

Overwriting occurs only if a method is part of the interface of the base class. That is, an object must be transformed up to its base class and the same method must be invoked. If a method is private, it is not part of the base class interface. It's just some code that's hidden in a class. If a private method exists in a base class and a public, protected, or package access method is created in a derived class with the same name, the method simply has the same name as the method in the base class and does not override the base class 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.

Final class

When a class is defined as final, it indicates that you do not intend to inherit from that class and that no one else is allowed to do so. In other words, for some reason, you never have to make any changes to the design of this class, or for security reasons, you don't want it to have subclasses. Such as:


package finalPackage;
class SmallBrain {
}
final class Dinosaur {
  int i = 7;
  int j = 1;
  SmallBrain x = new SmallBrain();
  void f() {
    System.out.println("Dinosaur.f()");
  }
}
// ! class Further extends Dinosaur {}
// error: Cannot extend final class 'Dinosaur'

public class Jurassic {
  public static void main(String[] args) {
    Dinosaur n = new Dinosaur();
    n.f();
    n.i = 40;
    n.j++;
    System.out.println("n.i="+n.i);
    System.out.println("n.j="+n.j);
  }
  
}

Note that the domain of a final class can be chosen to be or not to be final depending on the individual's wishes. The same rules apply to fields that are defined as final, whether or not the class is defined as final. However, since final is uninheritable, methods in classes that are modified with final are implicitly specified as fianl because you cannot override them. You can add final to a method ina fianl class, but that doesn't make any sense.

Conclusion:

Depending on the program context, the Java keyword final has the meaning of "this is immutable" or "final state," and it can modify non-abstract classes, non-abstract class member methods, and variables. You may need to prevent change for two reasons: design or efficiency.
Final classes cannot be inherited, there are no subclasses, and methods in final classes are final by default.
Final methods cannot be overridden by methods in subclasses, but they can be inherited.
Final member variables represent constants that can only be assigned once, and the value does not change after the assignment.
Final cannot be used to modify a constructor.
Note: private member methods of a parent class cannot be overridden by subclass methods, so methods of private type are final by default.

I hope this article has been helpful to you in Java programming.


Related articles: