Three look-alike terms—final, finally, and finalize—serve completely different purposes in Java. Confusing them can cause compiler errors, logic bugs, or poor resource management. This article breaks down each term, shows real code, and highlights interview-ready differences.
⚡ Quick Comparison Table
Feature | final (Keyword) |
finally (Block) |
finalize() (Method) |
---|---|---|---|
Category | Modifier keyword | Exception-handling block | java.lang.Object method |
Applied To | Variables, methods, classes | try-catch-finally |
Any class (via override) |
Main Purpose | Prevent change (immutability, no override, no subclass) | Ensure cleanup code always runs | Custom cleanup before garbage collection |
When Executes | Compile-time rule | At runtime, after try / catch |
When GC decides to reclaim object |
Can It Fail to Run? | N/A | Runs unless JVM crashes | Might never run if GC not triggered |
Typical Use-Case | Constants, secure APIs | Close files, release locks | Rare—legacy resource cleanup |
1️⃣ The final
Keyword
1.1 Final Variable (Constant)
final String NAME = "John";
// NAME = "Jim"; // ❌ compile-time error
1.2 Final Method (No Override)
class Base {
final void greet() { System.out.println("Hello"); }
}
class Sub extends Base {
// void greet() { } // ❌ cannot override final method
}
1.3 Final Class (No Subclassing)
public final class ImmutableUtility { }
class Child extends ImmutableUtility { } // ❌ compile-time error
When to use: Immutability (constants), security APIs, or preventing subclassing for design reasons.
2️⃣ The finally
Block
Guarantees execution regardless of whether an exception is thrown.
try {
int result = 300 / divisor;
} catch (ArithmeticException ex) {
System.out.println("Divide by zero!");
} finally {
System.out.println("Cleanup runs here.");
}
Best Practices
- Close streams (
try-with-resources
preferred in Java 7+). - Release locks or database connections.
- Avoid
return
statements insidefinally
(can swallow exceptions).
3️⃣ The finalize()
Method
Called by the Garbage Collector once before object destruction.
@Override
protected void finalize() throws Throwable {
System.out.println("finalize() called");
}
public static void main(String[] args) {
new FinalizeDemo();
System.gc(); // Hint to run GC
}
Why You Should Avoid It
- Execution time is unpredictable.
- Adds GC overhead.
- Deprecated for removal (JEP 421, Java 18).
Modern Alternative: Use
try-with-resources
orCleaner
API for explicit cleanup.
🚦 When to Use What
Scenario | Use |
---|---|
Create immutable constant | final variable |
Prevent method override | final method |
Guarantee resource release | finally block (or TWR) |
Legacy cleanup before GC | finalize() (discouraged) |
🧠 Interview Nuggets
-
Q: Can
finally
execute ifSystem.exit(0)
is called intry
?
A: No, JVM exits beforefinally
runs. -
Q: Is
finalize()
guaranteed to run?
A: No, JVM may terminate without calling it. -
Q: Can a
final
object’s internal state change?
A: Yes—final
prevents reassigning the reference, not object mutation.
🧭 Java Version Notes
Feature | Status / Version | Notes |
---|---|---|
final keyword |
Since Java 1.0 | Core language feature |
finally block |
Since Java 1.0 | Core language feature |
finalize() |
Deprecated since Java 9, forRemoval=true since Java 18 | Use Cleaner or TWR instead |
📝 Summary
final
→ immutability or inheritance control (compile-time).finally
→ guaranteed cleanup aftertry
/catch
(runtime).finalize()
→ legacy GC callback, avoid in modern code.
Takeaway: Prefer
final
for design clarity,finally
(or better,try-with-resources
) for cleanup, and avoidfinalize()
in new code.