final vs finally vs finalize in Java

Learn the difference between final, finally and finalize in Java, with examples, when finally does not run, and what replaces deprecated finalize().

Comparison of final, finally and finalize in Java

The words final, finally and finalize look alike but have nothing in common, because final is a modifier that prevents change, finally is a block that always runs after try, and finalize() is a deprecated method of Object. Interviewers like the question because the three similar names belong to unrelated features.

We use final every day for constants, immutable fields and classes that must not be extended. The finally block releases resources and restores state, although try-with-resources covers most of its uses. The finalize() method is a cleanup hook that is deprecated for removal, so we only need to recognize it and replace it.

The following example uses all three words in a few lines.

final int maxRetries = 3;               // final: cannot be reassigned
String status;
try {
    status = "attempts allowed: " + maxRetries;
} finally {
    status = "checked";                 // finally: runs after try
}
String result = status;                 // "checked"
// finalize(): never called here, and deprecated for removal since Java 18

Notice that only final and finally are keywords, whereas finalize is a plain method name. We go through each of the three, compare them in one table, and show what replaces finalize() in modern Java.

1. The final Keyword

The final modifier means that something cannot change after it is set. Its exact meaning depends on where we put it.

Comparison of final, finally and finalize in Java
Two keywords and one deprecated method with unrelated purposes
Applied toEffect
Local variable or parameterCan be assigned only once
FieldMust be assigned once, in its declaration, an initializer block or every constructor
MethodCannot be overridden in a subclass
ClassCannot be extended, such as String and Integer
final class Money {
    private final long cents;           // blank final, set in the constructor

    Money(long cents) {
        this.cents = cents;
    }

    final long cents() {
        return cents;
    }
}
Money price = new Money(1999);
long cents = price.cents();             // 1999

A final reference cannot point to another object, but the object itself can still change. A final List can still receive new elements, so a truly immutable field also needs an immutable object, such as one from List.of().

final List<String> tags = new ArrayList<>();
tags.add("sale");                       // allowed, the list changes
int tagCount = tags.size();             // 1
final List<String> fixed = List.of("new");
fixed.add("sale");                      // UnsupportedOperationException

Lambdas and anonymous classes can use local variables only if they are final or effectively final, which means they are never reassigned after initialization.

2. The finally Block

A finally block follows a try block, with or without catch blocks, and runs when the try block completes normally, throws an exception, or returns. It is the place for cleanup that must happen on every path.

static String readConfig(boolean fail) {
    StringBuilder log = new StringBuilder("open;");
    try {
        if (fail) {
            throw new IllegalStateException("bad file");
        }
        log.append("read;");
    } catch (IllegalStateException e) {
        log.append("error;");
    } finally {
        log.append("close");
    }
    return log.toString();
}
String ok = readConfig(false);          // "open;read;close"
String failed = readConfig(true);       // "open;error;close"

For files, streams, connections and locks, try-with-resources is shorter and safer than a hand-written finally block, because it closes every resource and keeps both exceptions when close() also fails.

2.1. When finally Does Not Run

The block runs in almost every case, but not when the JVM stops before reaching it. That happens with System.exit() or Runtime.halt() inside the try block, a JVM crash, a killed process, or a daemon thread that is still running when the JVM exits.

2.2. Never Return from finally

A return inside finally replaces the value returned by try and discards any exception thrown there. The compiler accepts it and warns only with -Xlint:finally, although IDEs flag it, so we keep finally blocks free of return statements.

@SuppressWarnings("finally")
static int lostException() {
    try {
        throw new IllegalStateException("lost");
    } finally {
        return -1;                      // the exception disappears
    }
}
int code = lostException();             // -1, no exception reaches the caller

3. The finalize() Method

The Object.finalize() method was meant to run cleanup code before the garbage collector reclaims an object. It never worked well, because the JVM gives no guarantee when, or whether, it runs, and a slow finalizer can delay the collection of many objects. The guide on why not to use finalize() explains these problems in detail.

The method was deprecated in Java 9, and JEP 421 deprecated it for removal in Java 18. Since Java 18, we can test a program without finalization by starting it with –finalization=disabled, which shows whether anything still depends on it.

Two tools replace it. The AutoCloseable interface with try-with-resources releases resources at a known point, and the Cleaner class runs a cleanup action as a fallback when an object becomes unreachable.

class TempFile implements AutoCloseable {
    private static final Cleaner CLEANER = Cleaner.create();
    private final Cleaner.Cleanable cleanable;
    static final List<String> LOG = new ArrayList<>();

    TempFile(String name) {
        cleanable = CLEANER.register(this, () -> LOG.add("deleted " + name));
    }

    @Override
    public void close() {
        cleanable.clean();               // runs the action once
    }
}
try (TempFile file = new TempFile("report.tmp")) {
    TempFile.LOG.add("writing");
}
String log = TempFile.LOG.toString();   // "[writing, deleted report.tmp]"

The cleanup lambda must not refer to the TempFile object itself, otherwise the object stays reachable and the action never runs.

4. final vs finally vs finalize

The table compares the three, which is the short answer to the interview question.

finalfinallyfinalize()
What it isKeyword, a modifierKeyword, a block after tryMethod of Object
PurposePrevents reassignment, overriding or extensionRuns cleanup code on every exit from tryCleanup before garbage collection
RunsAt compile time, as a rule the compiler checksEvery time the try block endsMaybe, at an unknown time
Status in Java 25Used everywhereUsed, often replaced by try-with-resourcesDeprecated for removal

5. final, finally and finalize FAQs

Interviewers and developers ask these follow-up questions.

5.1. Is finalize() removed in Java 25?

Not yet. It is deprecated for removal and still exists, but new code should not use it. The –finalization=disabled option lets us check that a program works without it.

5.2. Does finally run if catch throws an exception?

Yes. The finally block runs first, and the exception from catch continues to the caller, unless finally throws its own exception or returns.

5.3. Can a final variable be assigned later?

Yes, once. A blank final local variable can be assigned in a later statement, and a blank final field must be assigned in every constructor or in an initializer block.

5.4. Can we make a constructor final?

No. Constructors are not inherited, so they cannot be overridden, and final has no meaning for them.

6. Conclusion

The final modifier prevents reassignment, overriding and extension, but a final reference can still point to a mutable object. The finally block runs on every exit from try, except when the JVM stops, and it should never contain return.

The finalize() method is deprecated for removal. We release resources with AutoCloseable and try-with-resources, and use Cleaner only as a fallback for objects that a caller forgets to close.

7. References

Happy Learning !!

Source Code on Github

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.