View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All
View All

Finalize Method in Java: Complete Guide with Examples and Best Practices

Updated on 24/04/20254,217 Views

Introduction

The finalize method in Java plays a crucial role in resource management, though it has limitations every developer should understand. This guide explains what the finalize method is, how it works with garbage collection, and provides practical examples for real-world scenarios.

Curious about where Java can take you? Enroll in upGrad's Software Engineering course to unlock career opportunities in software development.

What is Finalize Method in Java?

The finalize method in Java Programming is a special method that gets called before an object is garbage collected. It's designed to perform cleanup operations on objects before they're permanently removed from memory. The finalize method comes protected under the Object class with the signature:

protected void finalize() throws Throwable

This method gives objects one last chance to release resources that aren't automatically handled by the garbage collector, such as file handles, network connections, or database connections.


Take the next step in your career with IIIT-B’s Executive Diploma in Data Science & AI.

How Garbage Collection Works in Java

Before diving deeper into the finalize method, let's understand how garbage collection works in Java:

Java's garbage collector automatically reclaims memory occupied by objects that are no longer reachable or referenced. An object becomes eligible for garbage collection when:

  1. No references point to it - When all references to an object are removed, it becomes eligible for garbage collection.
  2. References are set to null - When all references to an object are explicitly set to null.
  3. References point to other objects - When all original references to an object are reassigned to other objects.

Here's a simple example showing how objects become unreferenced:

public class GarbageCollectionExample {
    public static void main(String[] args) {
        // Creating an object
        StringBuilder builder = new StringBuilder("Hello");
        
        // Making the object unreferenced by assigning null
        builder = null;
        
        // Suggesting garbage collection (not guaranteed)
        System.gc();
    }
}

Output:

[No visible output unless finalize method is overridden]

When the reference builder is set to null, the StringBuilder object becomes eligible for garbage collection.

Want to integrate your Java skills with cloud and DevOps? Explore the Professional Certificate Program in Cloud Computing and DevOps to boost your backend and deployment expertise.

How Does the Finalize Method Work with Garbage Collection?

When the garbage collector determines an object is ready for collection, it doesn't immediately destroy it. Instead, the process works as follows:

  1. The garbage collector identifies objects with no live references
  2. For objects that override the finalize method, it places them in a finalization queue
  3. A separate finalization thread calls the finalize method on these objects
  4. Only after finalization completes (and assuming the object wasn't "resurrected") will the memory be reclaimed

Let's see this in action with a simple example:

public class FinalizeExample {
    public static void main(String[] args) {
        // Creating an object
        DatabaseConnection connection = new DatabaseConnection();
        
        // Using the connection
        connection.connect();
        
        // Making the object unreferenced
        connection = null;
        
        // Suggesting garbage collection
        System.gc();
        
        // Sleep to give GC time to run (for demonstration)
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class DatabaseConnection {
    private boolean isConnected = false;
    
    public void connect() {
        System.out.println("Database connection established");
        isConnected = true;
    }
    
    @Override
    protected void finalize() throws Throwable {
        try {
            if (isConnected) {
                System.out.println("Closing database connection in finalize method");
                // Close the connection logic would go here
                isConnected = false;
            }
        } finally {
            super.finalize();  // Always call superclass finalize
        }
    }
}

Output:

Database connection established

Closing database connection in finalize method

This example demonstrates a database connection that uses the finalize method to ensure connections are closed before the object is garbage collected. When the reference is nullified and garbage collection runs, the finalize method executes and closes the connection.

Real-World Example: Resource Management with Finalize Method

Let's explore a practical scenario where the finalize method can be useful. Consider a file handling system:

public class FileResourceExample {
    public static void main(String[] args) {
        // Create a file handler
        FileHandler handler = new FileHandler("important_data.txt");
        
        // Use the handler
        handler.writeData("This is important data");
        
        // Forget to close the handler (common mistake)
        handler = null;  // Reference lost
        
        // Force garbage collection (for demonstration)
        System.gc();
        System.runFinalization();
        
        // Program continues...
        System.out.println("Program continues execution...");
        
        // Sleep to give time for finalization (for demonstration)
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class FileHandler {
    private String filename;
    private boolean fileOpen;
    
    public FileHandler(String filename) {
        this.filename = filename;
        this.fileOpen = true;
        System.out.println("File opened: " + filename);
    }
    
    public void writeData(String data) {
        if (fileOpen) {
            System.out.println("Writing to file: " + data);
            // Actual file writing code would go here
        }
    }
    
    public void closeFile() {
        if (fileOpen) {
            System.out.println("Explicitly closing file: " + filename);
            fileOpen = false;
            // Actual file closing code would go here
        }
    }
    
    @Override
    protected void finalize() throws Throwable {
        try {
            if (fileOpen) {
                System.out.println("WARNING: File was not closed properly. Closing in finalize: " + filename);
                fileOpen = false;
                // Actual emergency file closing code would go here
            }
        } finally {
            super.finalize();
        }
    }
}

Output:

File opened: important_data.txt

Writing to file: This is important data

Program continues execution...

WARNING: File was not closed properly. Closing in finalize: important_data.txt

In this real-world example, the FileHandler class manages file resources. If a developer forgets to close the file explicitly (a common oversight), the finalize method acts as a safety net to ensure the file is closed eventually. This helps prevent resource leaks in production systems.

Final vs. Finally vs. Finalize Method in Java

These three similarly named concepts in Java serve different purposes:

Keyword/Method

Purpose

final

A keyword used to restrict modification of variables, methods, and classes

finally

A block used in exception handling that always executes regardless of whether an exception occurs

finalize()

A method called by the garbage collector before reclaiming an object's memory

When to Use Finalize Method in Java?

The finalize method in Java should be used sparingly and for specific purposes:

  1. Releasing non-memory resources: When your object holds resources like file handles, network sockets, or database connections that need explicit cleanup.
  2. Safety net for resource cleanup: As a backup mechanism when the primary cleanup method (like close()) isn't called.
  3. Debugging memory leaks: To identify when objects are being collected.

Here's an example of using finalize as a safety net for a network connection:

public class NetworkResourceExample {
    public static void main(String[] args) {
        // Scenario: Code in a try-with-resources block (recommended approach)
        try (NetworkConnection properConnection = new NetworkConnection("server1")) {
            properConnection.sendData("Hello, server!");
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        // Scenario: Improper connection handling (bad practice)
        NetworkConnection improperConnection = new NetworkConnection("server2");
        improperConnection.sendData("Hello again!");
        // Developer forgot to close connection
        
        // Force GC for demonstration
        improperConnection = null;
        System.gc();
        
        // Wait to observe finalization
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class NetworkConnection implements AutoCloseable {
    private String serverName;
    private boolean connected;
    
    public NetworkConnection(String serverName) {
        this.serverName = serverName;
        this.connected = true;
        System.out.println("Connected to " + serverName);
    }
    
    public void sendData(String data) {
        if (connected) {
            System.out.println("Sending to " + serverName + ": " + data);
        }
    }
    
    @Override
    public void close() {
        if (connected) {
            System.out.println("Properly closing connection to " + serverName);
            connected = false;
            // Actual connection closure would happen here
        }
    }
    
    @Override
    protected void finalize() throws Throwable {
        try {
            if (connected) {
                System.out.println("WARNING: Connection to " + serverName + " was not closed properly. Closing in finalize().");
                connected = false;
                // Emergency cleanup code
            }
        } finally {
            super.finalize();
        }
    }
}

Output:

Connected to server1

Sending to server1: Hello, server!

Properly closing connection to server1

Connected to server2

Sending to server2: Hello again!

WARNING: Connection to server2 was not closed properly. Closing in finalize().

This example contrasts proper resource management using try-with-resources with improper resource management that relies on finalize as a backup. The finalize method catches the developer's mistake of not closing the connection.

How to Override Finalize Method in Java?

To override the finalize method in Java, follow these steps:

  1. Declare a protected method named finalize
  2. Add the throws Throwable clause
  3. Use the @Override annotation
  4. Call super.finalize() in a finally block

Here's the proper way to override the finalize method:

public class CorrectFinalizeOverride {
    private String resourceName;
    private boolean resourceAcquired;
    
    public CorrectFinalizeOverride(String name) {
        this.resourceName = name;
        this.resourceAcquired = true;
        System.out.println("Resource acquired: " + resourceName);
    }
    
    // Proper way to release resources
    public void release() {
        if (resourceAcquired) {
            System.out.println("Explicitly releasing: " + resourceName);
            resourceAcquired = false;
        }
    }
    
    // Correct finalize override pattern
    @Override
    protected void finalize() throws Throwable {
        try {
            // Perform cleanup
            if (resourceAcquired) {
                System.out.println("WARNING: Resource not released properly: " + resourceName);
                resourceAcquired = false;
            }
        } finally {
            // Always call super.finalize() in finally block
            super.finalize();
        }
    }
    
    public static void main(String[] args) {
        // Case 1: Proper resource management
        CorrectFinalizeOverride obj1 = new CorrectFinalizeOverride("Resource1");
        obj1.release();
        
        // Case 2: Improper resource management
        CorrectFinalizeOverride obj2 = new CorrectFinalizeOverride("Resource2");
        // forgot to release
        
        // Make objects eligible for garbage collection
        obj1 = null;
        obj2 = null;
        
        // Request garbage collection
        System.gc();
        System.runFinalization();
        
        // Wait to observe finalization
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Output:

Resource acquired: Resource1

Explicitly releasing: Resource1

Resource acquired: Resource2

WARNING: Resource not released properly: Resource2

This example demonstrates the correct pattern for overriding the finalize method, ensuring proper cleanup even when developers forget to release resources explicitly.

Better Alternatives to Finalize Method in Java

Due to the limitations of the finalize method, Java has introduced better alternatives:

1. Try-with-resources (Java 7+)

public class TryWithResourcesExample {
    public static void main(String[] args) {
        // Resources are automatically closed when the try block exits
        try (FileOutputStream fos = new FileOutputStream("output.txt")) {
            fos.write("Hello World".getBytes());
            System.out.println("Data written to file");
        } catch (IOException e) {
            e.printStackTrace();
        }
        // FileOutputStream is automatically closed here
    }
}

Output:

Data written to file

The try-with-resources approach automatically closes resources that implement AutoCloseable, eliminating the need for finalize.

2. Cleaner API (Java 9+)

import java.lang.ref.Cleaner;

public class CleanerExample {
    // Create a cleaner
    private static final Cleaner cleaner = Cleaner.create();
    
    // Resource to be cleaned up
    private static class ResourceHolder implements Runnable {
        private String resourceName;
        
        ResourceHolder(String name) {
            this.resourceName = name;
            System.out.println("Resource acquired: " + resourceName);
        }
        
        @Override
        public void run() {
            System.out.println("Cleaning up resource: " + resourceName);
            // Actual cleanup code would go here
        }
    }
    
    private final Cleaner.Cleanable cleanable;
    private final ResourceHolder holder;
    
    public CleanerExample(String name) {
        this.holder = new ResourceHolder(name);
        this.cleanable = cleaner.register(this, holder);
    }
    
    // Explicit cleanup method (preferred way)
    public void close() {
        cleanable.clean();
    }
    
    public static void main(String[] args) {
        // Case 1: Proper resource management
        CleanerExample resource1 = new CleanerExample("Resource1");
        resource1.close();  // Explicitly clean
        
        // Case 2: Relying on automatic cleaning
        CleanerExample resource2 = new CleanerExample("Resource2");
        // No explicit cleanup
        
        // Make eligible for garbage collection
        resource1 = null;
        resource2 = null;
        
        // Suggest garbage collection
        System.gc();
        
        // Wait to observe cleaning
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Output:

Resource acquired: Resource1

Cleaning up resource: Resource1

Resource acquired: Resource2

Cleaning up resource: Resource2

The Cleaner API provides a more reliable and efficient mechanism for resource cleanup compared to the finalize method.

Can We Override Final Method in Java?

No, final methods in Java cannot be overridden. The final keyword prevents a method from being overridden in subclasses, ensuring its implementation remains constant throughout the inheritance hierarchy.

public class FinalMethodExample {
    // Final method that cannot be overridden
    public final void displayMessage() {
        System.out.println("This is a final method");
    }
}

class ChildClass extends FinalMethodExample {
    // This will cause a compilation error
    // @Override
    // public void displayMessage() {
    //     System.out.println("Trying to override final method");
    // }
}

Can We Overload Final Method in Java?

Yes, final methods in Java can be overloaded. Method overloading is about creating multiple methods with the same name but different parameters, which is allowed for final methods.

public class FinalMethodOverloadingExample {
    // Final method with one parameter
    public final void display(String message) {
        System.out.println("String message: " + message);
    }
    
    // Overloaded final method with different parameter
    public final void display(int number) {
        System.out.println("Integer number: " + number);
    }
    
    public static void main(String[] args) {
        FinalMethodOverloadingExample example = new FinalMethodOverloadingExample();
        example.display("Hello");  // Calls first method
        example.display(100);      // Calls second method
    }
}

Output:

String message: Hello

Integer number: 100

This example shows that final methods can be overloaded by changing the parameter list.

Best Practices for Resource Management in Java

  1. Use try-with-resources for automatic resource management
  2. Implement AutoCloseable for custom resources
  3. Prefer explicit cleanup methods like close() over finalize
  4. Use finalize as a last resort safety net
  5. Consider the Cleaner API for modern resource management
  6. Always call super.finalize() when overriding finalize

Conclusion

The finalize method in Java is like a safety net that catches resources before they're lost forever. It helps clean up things like open files and connections when programmers forget to do it themselves. But it's not reliable - it might run late or not at all.

Today's Java developers should use better tools like try-with-resources instead. Think of finalize as your backup plan, not your main strategy. When used correctly, it can help prevent resource leaks, but depending on it too much can cause more problems than it solves.

Always close your resources explicitly rather than hoping finalize will catch them. This makes your code safer, faster, and more predictable.

Faqs

1. What is finalize method in Java?

The finalize method in Java is a protected method of the Object class that is called by the garbage collector before reclaiming an object's memory, allowing for last-minute cleanup operations. It serves as the last opportunity for an object to release any system resources it might be holding before it's permanently removed from memory.

2. Can we override final method in Java?

No, final methods in Java cannot be overridden. The final keyword prevents method overriding in subclasses. This restriction is enforced at compile-time, meaning any attempt to override a final method will result in a compilation error rather than a runtime exception.

3. Can we overload final method in Java?

Yes, final methods in Java can be overloaded by creating methods with the same name but different parameters. This is because method overloading is resolved at compile time based on method signatures, whereas the final keyword only restricts runtime polymorphism through method overriding.

4. What is the use of finalize method in Java?

The finalize method in Java is used to perform cleanup operations on objects before they're garbage collected, especially for resources not automatically managed by Java. It helps prevent resource leaks by providing a mechanism to release external resources like file handles, database connections, and network sockets that the garbage collector doesn't know how to clean up on its own.

5. How many times can the finalize method be called for an object?

The finalize method is called at most once per object by the JVM, even if the object is later resurrected. This is an important limitation to understand, as it means you cannot rely on finalize for repeated cleanup operations on the same object throughout its potentially complex lifecycle.

6. Is finalize method deprecated in Java?

The finalize method was deprecated in Java 9 in favor of better alternatives like try-with-resources and the Cleaner API. The deprecation came after years of practical experience showed that finalize was problematic due to its unpredictable timing, performance impacts, and potential for introducing subtle bugs in applications.

7. What is the difference between final, finally, and finalize in Java?

Final is a keyword for constants, finally is an exception handling block, and finalize() is a method called before garbage collection. Though they sound similar, they serve completely different purposes in Java programming: final prevents modification, finally ensures code execution despite exceptions, and finalize helps with resource cleanup before memory reclamation.

8. What happens if an exception occurs in the finalize method?

If an exception occurs in the finalize method, it is ignored and finalization of that object terminates. This silent failure behavior makes debugging finalize-related issues challenging, as exceptions are swallowed by the runtime without any notification to the application, potentially masking serious problems.

9. Can finalize method prevent garbage collection?

Finalize can delay garbage collection by resurrecting the object, but cannot permanently prevent it. Object resurrection occurs when the finalize method creates new references to the object, temporarily saving it from garbage collection, but this only postpones rather than cancels the eventual memory reclamation process.

10. Is it recommended to use finalize method for resource cleanup?

No, it's not recommended to rely on finalize for critical resource cleanup due to its unpredictable execution. Modern Java programming best practices strongly favor explicit resource management patterns such as try-with-resources, which provide deterministic cleanup with better performance characteristics and fewer potential pitfalls.

11. What is final method in Java?

A final method in Java is a method that cannot be overridden by subclasses, ensuring its implementation remains constant throughout the inheritance hierarchy. Using the final keyword for methods is a common practice when the behavior defined in a method is essential to the class's proper functioning and altering it could break the class's contract or security guarantees.

image

Take the Free Quiz on Java

Answer quick questions and assess your Java knowledge

right-top-arrow
image
Join 10M+ Learners & Transform Your Career
Learn on a personalised AI-powered platform that offers best-in-class content, live sessions & mentorship from leading industry experts.
advertise-arrow

Free Courses

Explore Our Free Software Tutorials

upGrad Learner Support

Talk to our experts. We are available 7 days a week, 9 AM to 12 AM (midnight)

text

Indian Nationals

1800 210 2020

text

Foreign Nationals

+918068792934

Disclaimer

1.The above statistics depend on various factors and individual results may vary. Past performance is no guarantee of future results.

2.The student assumes full responsibility for all expenses associated with visas, travel, & related costs. upGrad does not provide any a.