Java final Keyword With Examples

Java final keyword

final is a reserved keyword dedicated to restricting customization to the member/entity it is tagged to. It is a non-access modifier which is applicable to class and its members(variables and methods). With common intention to restrict customization, it behaves differently according to tagged member/entity.

We will look at the use of final keyword with the followings.

  • Class
  • Variables
  • Methods

final keyword with class

Using final with class prevents it to get inherited by any other class. The reason for making a class final can have two reasons:

  • Restrict inheritance.  Once declared final, a class cannot be extended.
  • Create an Immutable class. It means that once the object is created, it cannot be changed. It is used in a concurrent environment to avoid any change in the object in synchronized blocks.

Example:

final class Parent{
   int parVal;
}

class Child extends Parent{
    int childVal;
}

public class ClassMain{
    public static void main(String args[]){
        Child obj = new Child();
    }
}

Output:

ClassMain.java:5: error: cannot inherit from final Parent
class Child extends Parent{
                    ^
1 error

final keyword with variables

Using final with a variable, makes it a constant, un-modifiable after its first assignment. It is important to initialize the final type variable by any of the following ways in order to avoid a compile-time error.

  1. Initialize right at the time of its declaration.
  2. Create a static block and initialize them.
  3. Initialize the variable inside every version of the constructor.
  4. Create an anonymous block/instance initializer block and initialize variables in it.

It is a convention to only use uppercase for the name of final variables to be identified easily.

Example:

class MyClass{
   //Version 1: Initialize right at the time of its declaration
   final int FINALVAR1=10;
   static final int FINALVAR2; //It is static, so that static block can refer and use it.
   final int FINALVAR3;
   final int FINALVAR4;

   //Version 2:     Create a static block and initialize them
   static{
       FINALVAR2 = 20;
   }
   //Version 3:     Initialize the variable inside every version of constructor
   MyClass(int initFinalVar){
       FINALVAR3 = initFinalVar;
   }
    //Version 4: Create an anonymous block/instance initializer block and initialize variables in it
   {
       FINALVAR4= 40;
   }
}

public class ClassMain{
    public static void main(String args[]){
        MyClass obj = new MyClass(30);
        System.out.println("FINALVAR1 = "+obj.FINALVAR1);
        System.out.println("FINALVAR2 = "+obj.FINALVAR2);
        System.out.println("FINALVAR3 = "+obj.FINALVAR3);
        System.out.println("FINALVAR4 = "+obj.FINALVAR4);

    }
}

Output:

FINALVAR1 = 10
FINALVAR2 = 20
FINALVAR3 = 30
FINALVAR4 = 40

Now let us try changing the values.

class MyClass{
   //Version 1: Initialize right at the time of its declaration
   final int FINALVAR1=10;
   static final int FINALVAR2; //It is static, so that static block can refer and use it.
   final int FINALVAR3;
   final int FINALVAR4;

   //Version 2:     Create a static block and initialize them
   static{
       FINALVAR2 = 20;
   }
   //Version 3:     Initialize the variable inside every version of constructor
   MyClass(int initFinalVar){
       FINALVAR3 = initFinalVar;
   }
    //Version 4: Create an anonymous block/instance initializer block and initialize variables in it
   {
       FINALVAR4= 40;
   }
}

public class ClassMain{
    public static void main(String args[]){
        MyClass obj = new MyClass(30);
        System.out.println("(BEFORE)FINALVAR1 = "+obj.FINALVAR1);
        System.out.println("(BEFORE)FINALVAR2 = "+obj.FINALVAR2);
        System.out.println("(BEFORE)FINALVAR3 = "+obj.FINALVAR3);
        System.out.println("(BEFORE)FINALVAR4 = "+obj.FINALVAR4);

        obj.FINALVAR1++;
        obj.FINALVAR2++;
        obj.FINALVAR3++;
        obj.FINALVAR4++;

        System.out.println("(AFTER)FINALVAR1 = "+obj.FINALVAR1);
        System.out.println("(AFTER)FINALVAR2 = "+obj.FINALVAR2);
        System.out.println("(AFTER)FINALVAR3 = "+obj.FINALVAR3);
        System.out.println("(AFTER)FINALVAR4 = "+obj.FINALVAR4);

    }
}

Output:

ClassMain.java:35: error: cannot assign a value to final variable FINALVAR1
        obj.FINALVAR1++;
           ^
ClassMain.java:36: error: cannot assign a value to final variable FINALVAR2
        obj.FINALVAR2++;
           ^
ClassMain.java:37: error: cannot assign a value to final variable FINALVAR3
        obj.FINALVAR3++;
           ^
ClassMain.java:38: error: cannot assign a value to final variable FINALVAR4
        obj.FINALVAR4++;
           ^
4 errors

final keyword with methods

Using final with methods, makes it override resistant. After inheriting a parent class, the child class can override the inherited methods. In order to restrict overriding to the inherited method, we use the final methods.

Example:

class Parent{
    final void printDetails(){
    System.out.println("I am parent version and cannot be overridden ");  
   }
}

class Child extends Parent{
    @Override
    void printDetails(){
    System.out.println("I am child version and will result in compile error"); 
   }
}

public class ClassMain{
    public static void main(String args[]){
        Parent obj = new Parent();
        obj.printDetails();

        obj = new Child();
        obj.printDetails();
    }
}

Output:

ClassMain.java:9: error: printDetails() in Child cannot override printDetails() in Parent
void printDetails(){
     ^
overridden method is final
1 error

Example 2:

This example illustrates that the final method is inherited but as an override-resistant method

class Parent{
    final void printDetails(){
    System.out.println("I am parent version and cannot be overridden ");  
   }
}

class Child extends Parent{
}

public class ClassMain{
    public static void main(String args[]){
        Parent obj = new Parent();
        obj.printDetails();       

        obj = new Child();
        obj.printDetails();
    }
}

Output:

I am parent version and cannot be overridden
I am parent version and cannot be overridden

This was an overview of the final keyword. We will discuss the Java Abstract classes and methods in the next lecture.