Java has this keyword — final. It can be applied to classes, methods, variables (including method parameters). Lets talk about final... - 1For a class, the final keyword means that the class cannot have subclasses, i.e. inheritance is forbidden... This is useful when creating immutable (unchangeable) objects. For example, the String class is declared as final.
public final class String {
}

class SubString extends String { // Compilation error
}
I should also note that the final modifier cannot be applied to abstract classes (those with the keyword abstract), because these are mutually exclusive concepts. For a final method, the modifier means that the method cannot be overridden in subclasses. This is useful when we want to prevent the alteration of the original implementation.
public class SuperClass {
    public final void printReport() {
        System.out.println("Report");
    }
}

class SubClass extends SuperClass {
    public void printReport() { //Compilation error
        System.out.println("MyReport");
    }
}
For variables of a primitive type, the final keyword means that the value, once assigned, cannot be changed. For reference variables, it means that after an object is assigned, you cannot change the reference to that object. This is important! The reference cannot be changed, but the object's state can be changed. Java 8 introduced a new concept: effectively final. It applies only to variables (including method parameters). The bottom line is that despite the clear absence of the final keyword, the variable's value does not change after initialization. In other words, the final keyword can be applied to such a variable without a compilation error. Effectively final variables can be used inside local classes (local inner classes), anonymous classes (anonymous inner classes), and streams (Stream API).
public void someMethod() {
    // In the example below, both a and b are effectively final, since they are assigned values only once:
    int a = 1;
    int b;
    if (a == 2) b = 3;
    else b = 4;
    // c is NOT effectively final since its value changes
    int c = 10;
    c++;

    Stream.of(1, 2).forEach(s-> System.out.println(s + a)); // OK
    Stream.of(1, 2).forEach(s-> System.out.println(s + c)); // Compilation error
}
Now, let's have a little interview. After all, the goal of completing the CodeGym course is to become a Java developer and find an interesting and well-paid job. So, let's begin.
  1. What can we say about an array that is declared final?

  2. We know that the String class is immutable: the class is declared final. A string value is stored in a char array that is marked with the keyword final.

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];
Can we replace a String object's value (without changing the reference to the object)? These are real interview questions. And practice shows that many candidates do not answer them correctly. Understanding how the final keyword is used, especially for reference variables, is very important. As you contemplate this, I'll make a small request to the CodeGym team. Please give the text editor the ability to add a block whose content can be shown/hidden when you click on it. Answers:
  1. An array is an object, so the final keyword means that once a reference to the array is assigned, the reference cannot be changed. That said, you can change the state of the object.

    final int[] array = {1, 2, 3, 4, 5};
    array[0] = 9;	 // OK, because we're changing the contents of the array: {9, 2, 3, 4, 5}
    array = new int[5]; // Compilation error
  2. Yes, we can. The main thing is to understand what the thorny final keyword means when used with objects. The Reflection API can be used to replace values.

import java.lang.reflect.Field;

class B {
    public static void main(String[] args) throws Exception {
        String value = "Old value";
        System.out.println(value);

        // Get the String class's value field
        Field field = value.getClass().getDeclaredField("value");
        // Make it mutable
        field.setAccessible(true);
        // Set a new value
        field.set(value, "CodeGym".toCharArray());

        System.out.println(value);

        /* Output:
         * Old value
         * CodeGym
         */
    }
}
Please note that if we had tried to change the final variable of a primitive type in this way, then nothing would have happened. I suggest that you convince yourself: create a Java class, for example, with a final int field and try to change its value using the Reflection API. Good luck to all!