Understanding Java’s final, finally, and finalize: Usage, Pitfalls, and Best Practices
This article explains the distinct purposes of Java's final keyword, finally block, and finalize method, shows how to use them with classes, methods, and variables, highlights scenarios where finally may not run, discusses finalize deprecation, and recommends modern resource‑management techniques like try‑with‑resources.
final
final can modify classes, methods, and variables to indicate they cannot be inherited, overridden, or reassigned.
public final class FinalClass {
// class content
}
// public class SubClass extends FinalClass { // compile‑time error }When a method is declared final, subclasses cannot override it:
public class BaseClass {
public final void display() {
System.out.println("This is a final method.");
}
}
public class DerivedClass extends BaseClass {
// public void display() { // compile‑time error }
}A final variable’s value cannot change after initialization:
public class MyClass {
public static final int MY_CONSTANT = 10;
}finally
finally defines a block that always executes during exception handling, regardless of whether an exception occurs, making it ideal for cleanup tasks such as closing JDBC connections or releasing locks.
try {
// code that may throw an exception
} catch (Exception e) {
// handle exception
} finally {
// cleanup code executed in all cases
}However, the finally block may be skipped in certain situations:
JVM abnormal termination : calling System.exit() aborts the JVM, preventing finally from running.
Infinite loops or deadlocks : if the code inside try never reaches the end, the finally block is never reached.
Out‑Of‑Memory errors : an OOM can terminate the program before finally executes.
public class FinallyExample {
public static void main(String[] args) {
try {
System.out.println("Try block");
System.exit(0); // forces JVM exit, finally will NOT run
} finally {
System.out.println("Finally block");
}
}
}finalize
finalize is a method invoked by the garbage collector before an object is reclaimed, allowing cleanup of native resources.
public class MyClass {
@Override
protected void finalize() throws Throwable {
try {
// resource cleanup code
} finally {
super.finalize();
}
}
}The finalize method is deprecated and discouraged because its execution is tied to the GC cycle, which can delay resource release and hinder rapid reclamation.
Modern Java code should prefer explicit resource management, such as the try‑with‑resources statement, which automatically closes any object implementing AutoCloseable or Closeable:
try (BufferedReader br = new BufferedReader(new FileReader("example.txt"))) {
// use the BufferedReader
} catch (IOException e) {
e.printStackTrace();
}This pattern ensures deterministic cleanup without relying on the unpredictable behavior of finalize.
Mike Chen's Internet Architecture
Over ten years of BAT architecture experience, shared generously!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
