top of page
Writer's pictureThe Tech Platform

What is Final in Java?

Java, a versatile and widely used programming language, offers a multitude of features to facilitate robust and maintainable code. Among its essential components is the "final" keyword, which plays a pivotal role in establishing immutability and controlling class inheritance. By designating variables, methods, and classes as final, developers can enforce constants, prevent unintended modifications, and safeguard class hierarchies.

In this article, we will discuss What is final in Java? What is the use of the final keyword in Java? We will also discuss the benefits of using the final keyword and some examples of how it can be used in code.


Table of content:

What is Final in Java?

The final keyword in Java is used to restrict the modification of a variable, method, or class. It can be used in several contexts to define an entity that can only be assigned once. Once a final variable has been assigned, it always contains the same value.


Here are the different uses of the final keyword in Java:

  • Final variables Final variables can only be assigned once. This is useful for declaring constants or other values that should not be modified. For example, the mathematical constant PI could be declared as a final variable.

  • Final methods Final methods cannot be overridden by subclasses. This is useful for methods that are part of a class's public API and should not be modified by subclasses. For example, the equals() method in the Object class is declared as final.

  • Final classes Final classes cannot be extended by subclasses. This is useful for classes that should not be extended. For example, the String class is declared as final.


Final Variables in Java

A final variable is a variable that can be assigned a value only once and cannot be changed thereafter. Once a final variable is initialized, its value remains constant throughout the program's execution. This means that any attempt to modify the value of a final variable after its initialization will result in a compilation error.


When a variable is declared as final, it becomes a constant in the context of Java. Constants are values that remain the same throughout the program's execution and are used to store data that should not be modified. By declaring a variable as final, you are explicitly indicating that its value should be set only once, and any subsequent attempt to modify it is not allowed.


Final variables are particularly useful when you want to define certain values in your code that should never change.


Examples of declaring and using final variables

Let's look at some examples of declaring and using final variables in Java:


Declaring a final variable:

final int MY_CONSTANT = 42;

In this example, MY_CONSTANT is declared as a final variable of type int and is initialized with the value 42. Once this line of code is executed, the value of MY_CONSTANT cannot be changed in any subsequent part of the program.


Using a final variable in calculations:

final double PI = 3.14159;
int radius = 5;
double area = PI * radius * radius;

In this example, we declare PI as a final variable to represent the mathematical constant π. We then use it in the calculation of the area of a circle, where the radius is multiplied by itself and then multiplied by PI. The value of PI remains constant throughout the calculation and cannot be modified.


Using final with object references:

final String greeting = "Hello, ";
String name = "John";
String message = greeting + name;

In this example, the greeting is a final variable representing a constant string "Hello, ". It is concatenated with the name variable to create a personalized message. The value of greeting remains constant, preventing any accidental changes.


Final Methods in Java

In Java, a final method is a method that cannot be overridden by any subclass. When a method is marked as final in a superclass, it means that its implementation in the superclass cannot be changed or extended in any of its subclasses. This has significant implications for class inheritance and method overriding in Java.


Inheritance is a fundamental concept in object-oriented programming, where a subclass inherits the properties and behaviors of its superclass. This allows subclasses to reuse and extend the functionality of their superclass by overriding its methods. Method overriding occurs when a subclass provides a specific implementation for a method that is already defined in its superclass.


However, there are scenarios where a superclass method's implementation should remain unchanged in all subclasses. These situations often arise when the method represents a crucial piece of functionality or when it is tightly coupled with the internal workings of the superclass. In such cases, we use the final keyword to declare the method as final, preventing any further overriding.


How declaring a method as final prevents it from being overridden in subclasses

When a method is marked as final in a superclass, any attempt to override that method in its subclasses will result in a compilation error. The Java compiler enforces this restriction to ensure that the method's behavior remains consistent throughout the class hierarchy.


Here's an example to illustrate the use of final methods:

class Shape {
    public final void draw() {
        System.out.println("Drawing a generic shape.");
    }
}

class Circle extends Shape {
    // Trying to override the final method will result in a compilation error.
    // This method cannot be overridden due to the final keyword in the superclass.
    // public void draw() {
    //     System.out.println("Drawing a circle.");
    // 
    }
}

In this example, the draw() method in the Shape class is marked as final. When we attempt to override this method in the Circle subclass, the compiler raises an error, preventing the override.


Practical use cases of final methods in Java code

  1. Template Method Pattern: Final methods are commonly used in the Template Method design pattern. In this pattern, a superclass provides a template for an algorithm, where certain steps are defined as final methods that cannot be changed by subclasses. Subclasses can then override other methods to customize the algorithm's behavior while keeping the core structure intact.

  2. Critical Operations: In certain classes, there might be methods that perform crucial operations, and their implementations should remain consistent in all subclasses. Marking such methods as final ensures that their behavior is not unintentionally modified in subclasses, thereby maintaining the integrity of the superclass's functionality.

  3. Security and Safety: Final methods are useful in scenarios where safety or security concerns require certain methods to remain unchanged. For example, a class handling cryptographic operations might have a final method that ensures secure key generation or encryption.


Final Classes in Java

In Java, a final class is a class that cannot be subclassed or extended by any other class. When a class is marked as final, it means that it has reached its final form, and no other class can inherit from it. This decision is deliberate and typically made to maintain the design integrity of the class, preventing any further extension or modification of its behavior.


Java's class hierarchy allows for class inheritance, where a subclass (also known as a derived class) can extend a superclass (also known as a base class) and inherit its properties and behaviors. The subclass can add its specific functionality or override existing methods from the superclass.


However, there are situations where it is desirable to prevent further extension of a particular class. This is typically the case when a class has been designed to serve as a complete, standalone entity, and there is no need to modify its behavior or create specialized subclasses. Marking a class as final explicitly communicates that it should not be extended, ensuring that its functionality remains intact.


When a class is declared as final, the Java compiler enforces this restriction, and any attempt to create a subclass of the final class will result in a compilation error.



Let's consider a few examples to demonstrate the use of final classes:


Example 1: A final class cannot be subclassed.

final class Vehicle {
    // Class implementation
}

// Trying to create a subclass of the final class will result in a compilation error.
class Car extends Vehicle {
    // Subclass implementation
}

In this example, the Vehicle class is marked as final, and when we attempt to create a subclass Car, the compiler raises an error because we cannot extend a final class.


Example 2: Preventing extension of utility classes.

final class MathUtils {
    public static int add(int a, int b) 
    {
        return a + b;
    }

    // Other utility methods
}

// Trying to extend the final utility class will result in a compilation error.
class AdvancedMathUtils extends MathUtils {
    // Subclass implementation
}

In this example, the MathUtils class contains utility methods for basic mathematical operations. By marking it as final, we ensure that no other class can extend it. This prevents accidental subclassing and maintains the integrity of the utility class's functionality.


Example 3: Final classes in Java standard library.

The Java standard library contains several final classes that serve as building blocks for various functionalities. One such example is the String class. By making String final, Java ensures that its behavior remains consistent and prevents any unwanted modifications to the core string functionality.

final String myString = "Hello, World!";

In this example, String is a final class, and we create a string object myString. Since String is final, we cannot create a subclass of it.


Benefits of Using Final in Java

There are many benefits to using the final keyword in Java. Here are some of the most important:


1. Improved code readability and maintainability: The final keyword can help to make code more readable and maintainable by making it clear to other developers that certain values or methods should not be changed. This can help to prevent errors in code and make it easier to understand how the code works.


For example,

  • Declaring a constant as final makes it clear to other developers that the value of the constant should not be changed.

  • Declaring a method as final prevents subclasses from overriding the method, which can help to prevent errors in code.


2. Improved performance: In some cases, using the final keyword can improve the performance of code.


For example,

  • Using a final variable in a loop can help the compiler optimize the code and make it run faster.

  • Declaring a class as final can help the compiler optimize the class and make it smaller.


3. Increased security: The final keyword can be used to increase the security of code by preventing malicious code from modifying sensitive data or behavior.


For example,

  • Declaring a class as final prevents malicious code from extending the class and injecting new code into the class.

  • Declaring a method as final prevents malicious code from overriding the method and changing its behavior.


Comparison with other Java Keywords

Java provides several keywords that serve different purposes and play distinct roles in class and method definitions. Among them, "final," "static," and "abstract" are essential and frequently used keywords, each serving a specific purpose.

Feature

Final

Static

Abstract

Functionality

The "final" keyword is used to indicate that a variable, method, or class is unchangeable or cannot be further extended.

The "static" keyword is used to define class-level members (variables and methods) that belong to the class rather than individual instances of the class.

The "abstract" keyword is used to define abstract classes and methods, which are incomplete and must be implemented by their subclasses.

Variable

A final variable represents a constant value that cannot be modified once initialized.

A static variable is shared among all instances of a class, and its value remains the same for all objects of that class.

-

Method

A final method in a class cannot be overridden by any subclass, ensuring that its behavior remains consistent throughout the class hierarchy.

A static method belongs to the class itself and can be invoked using the class name, without creating an instance of the class.

An abstract method is declared without implementation in the abstract class and must be implemented by its concrete subclasses.

Classes

A final class cannot be subclassed, meaning no other class can extend it.

Unlike "final," the "static" keyword is not used to modify classes. Classes are always at the foundation of the inheritance hierarchy.

An abstract class cannot be instantiated directly; it serves as a blueprint for subclasses to inherit and implement the abstract methods defined within.

Modifiability

It is used to prevent modification, ensuring that a variable's value, method's behavior, or a class's extension remains unchanged.

It is used to define class-level members, allowing them to be accessed without creating instances of the class. It doesn't restrict modification but rather promotes class-level access.

It is used to create incomplete classes and methods that must be implemented by their subclasses. It promotes extension by enforcing implementation.

Class Inheritance

Final restricts class inheritance; a final class cannot be subclassed.

Static is not used to modify classes, and it doesn't affect inheritance. Classes are always at the base of the inheritance hierarchy.

It promotes class inheritance; abstract classes serve as blueprints for subclasses to extend and implement.

Method Overriding

Prevents method overriding; final method cannot be overridden in subclasses

It is not used to modify method, and it does not impact method overriding. However, static methods cannot be overridden but can be hidden by methods in subclasses.

It encourages method overriding; abstract methods must be implemented in concrete subclasses.


When to use Final in Java?

Knowing when to use "final" is crucial for writing robust and maintainable code. Here are some situations where you should consider using the "final" keyword:


Constants: When you want to define a constant value that should not be changed throughout the program's execution, use "final" for declaring constants. This ensures that the value remains constant and cannot be accidentally modified.

final int MAX_VALUE = 100; 

Immutability: For variables that should be set only once and not modified afterward, use "final" to make them immutable. This helps prevent unintentional changes to critical data and improves code reliability.

final String username = "john_doe"; 

Thread Safety: In multithreaded environments, using "final" for shared variables can ensure thread safety. Once initialized, the value of a final variable cannot be changed, eliminating potential data inconsistency issues.

final AtomicInteger counter = new AtomicInteger(0); 

Final Methods: When you want to prevent a method from being overridden in subclasses, mark the method as "final." This is useful when certain methods represent crucial functionality that should not be altered.

class Parent {     
    public final void display() 
    {         
        // Method implementation     
    } 
} 

Final Classes: For classes that should not be subclassed, declare them as "final" to prevent any further extension. This can help maintain the design integrity of certain classes.

final class UtilityClass {     // Class implementation } 

Template Method Pattern: In the Template Method design pattern, certain methods are declared as final to define the structure of an algorithm, preventing subclasses from modifying the core behavior.

abstract class Template 
{     
    public final void execute() {         
        // Common steps         
        doStep1();         
        doStep2();         
        // Common steps     
    }     
    protected abstract void doStep1();     
    protected abstract void doStep2(); 
} 

Security and Safety: For sensitive operations like cryptographic functions or authentication mechanisms, you can use "final" to ensure the integrity and safety of these methods.


Conclusion

Remember that using "final" should be done judiciously and with careful consideration of the design and requirements of your code. Overusing "final" may lead to code that is difficult to maintain and modify. By applying "final" thoughtfully, you can improve code quality, make your code more predictable, and prevent unintended changes, ultimately leading to more reliable and secure Java applications.

Comentarios


bottom of page