Final Keyword in Java - A Beginner's Guide

Tutorial Playlist

Final Keyword in Java

In Java, the final keyword serves as a modifier that can be applied to classes, methods, and variables, indicating that they are immutable, unextendable, or unmodifiable.

  • Final Variables: A final variable, once assigned a value, cannot be changed. This is particularly useful when you want to ensure that a certain value remains constant throughout the program’s execution. For instance:
final int MAX_VALUE = 100;

Any attempt to modify MAX_VALUE will result in a compilation error.

  • Final Methods: When a method is marked as final, it cannot be overridden by any subclass. This is commonly used in scenarios where you want to prevent the alteration of a critical piece of functionality in a class. For instance:
class Parent {
    final void doSomething() {
        // ...
    }
}

A subclass attempting to override doSomething() will result in a compilation error.

  • Final Classes: When a class is declared final, it cannot be extended by any other class. This is often employed for classes that should not be subclassed due to security, performance, or design concerns. For instance:
final class Singleton {
    // ...
}

No other class can inherit from the Singleton class.

Learn the Java Fundamentals and start building your own applications today!

Final Keyword in Java Examples

Certainly, let’s explore more examples to illustrate the usage of the final keyword in different contexts within Java.

Example 1: Final Variables

public class Circle {

    final double PI = 3.14159;

    double radius;

    Circle(double radius) {

        this.radius = radius;

    }

    double calculateArea() {

        return PI * radius * radius;

    }

}

In this example, the PI constant is marked as final to ensure that its value remains constant throughout the program. This prevents accidental modifications that might lead to incorrect calculations.

Check out the Java Strings blog to enhance your knowledge!

Example 2: Final Methods

public class Vehicle {

    void startEngine() {

        // Engine startup logic

    }

    final void stopEngine() {

        // Engine shutdown logic

    }

}

class Car extends Vehicle {

    // Cannot override stopEngine() method

    // ...

}

Here, the stopEngine() method is marked as final, indicating that it cannot be overridden by subclasses like Car. This guarantees that the engine shutdown logic remains consistent across all subclasses.

Example 3: Final Classes

final class MathUtils {

    static int add(int a, int b) {

        return a + b;

    }

    // Cannot extend the MathUtils class

}

In this example, the MathUtils class is marked as final, preventing any other class from extending it. This is often done for utility classes to avoid any unintended alterations to their core functionality.

Learn how to use Methods in Java to write more modular and reusable code!

Example 4: Final Variables in Lambdas

public class TaxCalculator {
    final double TAX_RATE = 0.15;
    double calculateTax(double income) {
        return income * TAX_RATE;
    }
}

Final variables within lambdas are also important, as they ensure that the variable retains its value when used inside a lambda expression. This can help prevent bugs caused by unintentional variable modifications within lambda functions.

Read on:- Super Keyword in Java!

Example 5: Final Parameters in Methods

public class StringUtils {

    static String capitalize(final String input) {

        // Even if someone tries to modify 'input' within the method, it won't affect the original argument.

        return Character.toUpperCase(input.charAt(0)) + input.substring(1);

    }

}

Using final with method parameters ensures that the parameter cannot be modified within the method body. This can be helpful to prevent accidental changes and maintain clear code semantics.

Example 6: Immutable Classes

public final class ImmutablePerson {
    private final String name;
    private final int age;
    public ImmutablePerson(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
}

In this example, the ImmutablePerson class showcases the concept of immutability. The final keyword is used to declare the instance variables, ensuring that once an object is created, its state cannot be changed.

Benefits of Using the Final Keyword in Java

  • Security: By marking certain methods or classes as final, you prevent unintended changes that could potentially compromise the security of your application.
  • Performance: Final methods and classes can enable certain compiler optimizations, leading to improved performance.
  • Code Maintenance: Final variables help in maintaining a consistent value throughout the program, reducing the chances of introducing bugs due to unintended value changes.
  • Design Intent: The final keyword also serves as a way to communicate your design intent to other developers, indicating that certain parts of the code should not be altered.

Pitfalls and Considerations

  • Overuse: While the final keyword is powerful, excessive use can lead to rigid code that’s hard to refactor or extend. Use it judiciously, focusing on areas where immutability is crucial.
  • Testing: Final classes and methods might be challenging to mock for unit testing. Consider this while designing your classes.
  • Inheritance: While final classes prevent inheritance, they can limit code reuse. Be sure to balance the benefits of code inheritance with the need for immutability.
  • Thread Safety: Final variables can contribute to thread safety, but they’re not a silver bullet. Proper synchronization is still necessary in multi-threaded scenarios.

Conclusion

The final keyword in Java is a versatile tool that promotes code stability, security, and performance. By using it judiciously in variables, methods, and classes, you can enhance your code’s robustness and maintainability. However, it’s important to strike a balance between enforcing immutability and allowing flexibility for future code changes. Understanding the nuances of the final keyword empowers developers to create well-designed, reliable Java applications.