The final Keyword in Java
Question
Explain the different uses of the final
keyword in Java, including its application to variables, methods, and classes. What are the implications and best practices for using final
?
Answer
The final
keyword in Java is used to restrict the modification of variables, methods, and classes. It's a powerful tool for creating immutable objects and preventing inheritance or method overriding.
1. Final Variables
Local Variables
public class FinalVariableExample {
public void method() {
final int x = 10; // Must be initialized
// x = 20; // Compilation error
}
}
Instance Variables
public class FinalInstanceVariable {
private final int value; // Must be initialized in constructor or declaration
public FinalInstanceVariable(int value) {
this.value = value; // Initialize in constructor
}
// value = 20; // Compilation error
}
Static Variables
public class FinalStaticVariable {
private static final double PI = 3.14159; // Must be initialized at declaration
// PI = 3.14; // Compilation error
}
2. Final Methods
public class FinalMethodExample {
class Parent {
public final void method() {
System.out.println("Cannot be overridden");
}
}
class Child extends Parent {
// @Override
// public void method() {} // Compilation error
}
}
3. Final Classes
public final class FinalClass {
// Cannot be extended
}
// class Child extends FinalClass {} // Compilation error
4. Final Parameters
public class FinalParameter {
public void process(final String input) {
// input = "new value"; // Compilation error
System.out.println(input);
}
}
5. Final Reference Variables
public class FinalReference {
public static void main(String[] args) {
final StringBuilder sb = new StringBuilder("Hello");
sb.append(" World"); // Valid - modifying object
// sb = new StringBuilder(); // Invalid - changing reference
}
}
6. Final and Immutability
Creating Immutable Objects
public final class ImmutablePerson {
private final String name;
private final int age;
private final List<String> hobbies;
public ImmutablePerson(String name, int age, List<String> hobbies) {
this.name = name;
this.age = age;
this.hobbies = new ArrayList<>(hobbies); // Defensive copy
}
public String getName() { return name; }
public int getAge() { return age; }
public List<String> getHobbies() {
return Collections.unmodifiableList(hobbies); // Unmodifiable view
}
}
7. Final in Anonymous Classes
public class AnonymousClass {
public static void main(String[] args) {
final int x = 10; // Must be final to use in anonymous class
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println(x); // Can access final variable
}
};
}
}
8. Final and Lambda Expressions
public class LambdaExample {
public static void main(String[] args) {
final int x = 10; // Must be final or effectively final
Runnable r = () -> System.out.println(x);
}
}
9. Final and Thread Safety
public class ThreadSafeExample {
private final Map<String, String> cache = new HashMap<>();
public void initializeCache() {
cache.put("key1", "value1");
cache.put("key2", "value2");
}
public String getValue(String key) {
return cache.get(key);
}
}
10. Best Practices
-
Use final for Constants
public class Constants { public static final int MAX_RETRY_ATTEMPTS = 3; public static final String DEFAULT_ENCODING = "UTF-8"; }
-
Use final for Immutable Objects
public final class ImmutablePoint { private final int x; private final int y; public ImmutablePoint(int x, int y) { this.x = x; this.y = y; } public int getX() { return x; } public int getY() { return y; } }
-
Use final for Method Parameters
public class ParameterExample { public void process(final String input) { // Prevents accidental modification System.out.println(input); } }
11. Common Pitfalls
-
Final Reference Variables
public class FinalReferencePitfall { public static void main(String[] args) { final List<String> list = new ArrayList<>(); list.add("item"); // Valid // list = new ArrayList<>(); // Invalid } }
-
Final and Collections
public class CollectionPitfall { private final List<String> list = new ArrayList<>(); public List<String> getList() { return list; // Returns mutable reference } // Better approach public List<String> getList() { return Collections.unmodifiableList(list); } }
12. Performance Considerations
-
JVM Optimization
public class OptimizationExample { private final int value = 42; public int getValue() { return value; // May be inlined by JVM } }
-
Thread Safety
public class ThreadSafetyExample { private final Object lock = new Object(); public void synchronizedMethod() { synchronized(lock) { // Thread-safe code } } }
13. Design Patterns Using final
-
Singleton Pattern
public class Singleton { private static final Singleton INSTANCE = new Singleton(); private Singleton() {} public static Singleton getInstance() { return INSTANCE; } }
-
Builder Pattern ```java public class Person { private final String name; private final int age;
private Person(Builder builder) { this.name = builder.name; this.age = builder.age; }
public static class Builder { private String name; private int age;
public Builder name(String name) { this.name = name; return this; } public Builder age(int age) { this.age = age; return this; } public Person build() { return new Person(this); }
} }