When To Use The Final Keyword In Java

In this tutorial we will examine the final keyword in Java.

The Java final keyword allows you to enforce certain restrictions on the code that you write.

In particular the final keyword is mainly used to place restrictions on Java code. For example, it can be used to make variables immutable. Alternatively, it can be used to enforce a security policy at the code level, to prevent malicious code from overriding a critical block of code.

The final keyword can be applied to the following constructs:

  • class
  • method
  • method / constructor parameters
  • static variables
  • instance variables
  • local variables

Final Variables

The final keyword can be applied to instance variables and static variables of a class. Once a final variable is initialized it cannot be reassigned.

Final Static Variables

Typically, a static variable is defined with the final keyword to create a constant. These constants can be global or local to a scope – such as within a class. Normally, constants are created for strings, and primitive or primitive wrapper types.

1
public static final Integer ERROR_CODE_UNEXPECTED = 999; 

Sometimes, we may also create constants for immutable reference types.

1
public static final TemporalAdjuster INSTANCE = new NextBiannualTemporalAdjuster();

Above we create a constant INSTANCE for the NextBiannualTemporalAdjuster class. This is a common pattern found in code, that exposes a single instance of a type. One benefit of this approach is to reduce memory consumption.

Final static variables must be either initialized when they are declared as the INSTANCE variable above, or in a static initializer block.

1
2
3
4
5
public static final TemporalAdjuster INSTANCE;

static {
    INSTANCE = new NextBiannualTemporalAdjuster();
}

Final Instance Variables

Final instance variables are used to define variables of a class as immutable. This is handy approach when we wish to create immutable types, or define certain variables that cannot be modified once assigned.

Just like static variables, final instance variables must be initialized when they are declared or in a class constructor.

1
2
3
public final class ImmutableTypeExample {
    private final float x = 0;
}

Above the instance variable x is initialized with the value 0. Alternatively, it can be initialized in the class constructor.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public final class ImmutableTypeExample {
    private final float x;
    private final float y;

    public ImmutableTypeExample(final float x, final float y) {
        this.x = x;
        this.y = y;
    }

    public float getX() {
        return x;
    }

    public float getY() {
        return y;
    }
}

Local Variables

Local variables can also be marked as final. This is a useful technique when we wish to say that a variable defined in a method or a constructor cannot be changed. Applying this technique is a good practice, as it can help to reduce bugs in code, and increase readability.

1
2
3
4
public int add(final int x, final int y) {
    final int sum = x + y;
    return sum;
}

Above, if we try to reassign the variable sum, which will result in a compile time error. Moreover, final local variables must be initialized in the scope in which they are declared.

Final Primitive Types Versus Reference Types

There is an important difference between final primitive types and reference types. The difference should be obvious, if you are familiar with the difference between the two types. When we mark a primitive type as final its value cannot be changed, because a primitive type stores it value.

1
2
final int x = 0;
x = 1; //compile error

However, when we mark a reference type as final, only the reference stored in the variable cannot be changed. The object to which the reference points can still be mutated, unless it is immutable.

1
2
3
4
5
final List<String> listOfStrings = new ArrayList<String>();     

//compile time error
//listOfStrings = new ArrayList<>();
listOfStrings.add("New String");

Above, we are not able to reassign the listOfStrings variable as the reference must remain constant. However, we can still mutate the object to which its points. For example, above we add a new String.

Final Classes

When a class is marked final it cannot be extended. If you look at the Java SDK you will find plenty of examples of classes marked with final.

This approach is used when we create immutable types, to prevent a subclass from changing the immutability constraints that we define. For example, without marking a class as final it is open for extensibility and a programmer could do the following:

1
2
3
4
5
6
7
public class ImmutableTypeSubClass  extends ImmutableTypeExample{

    public float getX() {
        y = x + 1;
        return x;
    }
}

Above the programmer changes the value of the variable y, breaking the immutability constraints that we applied.

Another reason for marking a class as final is for security. This is to prevent types and code being manipulated in unpredictable ways. For example, Java API developers use this approach often.

1
2
3
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
}

Above the String class is marked final to prevent any unwanted side effects.

Another reason is that inheritance provides a weak form of re-usability. In Java unless explicitly done, classes are open for extension by default. This can cause some issues. Imagine that you have created a library and a client extends one of your classes. In a future release you modify this class. The modification you performed breaks the client code.

However, if we had of marked this class as final we explicitly tell the client that they should not be extending from that class.

Final Methods

Final methods are useful, when we want to allow a class to be extended, but to prevent certain methods from being overridden in a subclass. For example consider, the ClassLoader class in Java.

The Classloader class is open for extension to allow custom class loading. However if we look at the class definition:

1
2
3
4
public abstract class ClassLoader {
    protected final Class<?> defineClass(String name, byte[] b, int off, int len)
    throws ClassFormatError
}

We can see that the defineClass method is marked final. This is to prevent a custom classloader from defining a class in contradiction to the JVM specification.

Final Method / Constructor Parameters

Final method / constructor parameters are similar to final variables. Parameters marked as final cannot be changed in the body of a method or constructor. This can be a useful technique to prevent bugs within your programs.

Conclusion

In this short tutorial we discussed the final keyword in Java. We looked at some of the reasons to mark a construct final, and when the final keyword can be used.