Java learning pathSection 9

Object Lifecycle

Trace instance creation and initialization, constructor order, reference reachability, GC eligibility, legacy finalization, and explicit resource cleanup.

1

Introduction to the Object Lifecycle

An object's lifecycle includes creation, initialization, use while reachable, changes in reachability, possible eligibility for collection, and eventual storage reclamation. These are conceptual stages, not events that occur on a fixed clock schedule.

Java automatically manages ordinary object memory, but files, sockets, database connections, and other resources have their own lifetimes. Losing the last reference is not a reliable way to close a resource promptly.

Finalization, where enabled and applicable, adds legacy complications such as possible resurrection. It is not a mandatory stage for every object and should not be used to design modern cleanup.

Creation and initialization lead to reachable use, then possible unreachability, GC eligibility, and later nondeterministic reclamation; resource closing follows a separate explicit path
Object-memory lifetime and explicit resource lifetime are related but distinct.
2

Object Creation and References

A class defines instance structure and behavior; new requests a new instance. Instance fields belong to each object, while static fields belong to the runtime class rather than being duplicated for every instance.

For Student student = new Student(), the class must be available and initialized when required, storage is obtained, fields receive defaults, and the initialization/constructor chain executes. If construction completes normally, the expression produces a reference that can be assigned to student.

The reference is not the object. Assigning second = first copies a reference and creates an alias, not a second Student. Java does not require one universal physical object layout, and optimization can sometimes avoid a materialized allocation while preserving semantics.

Reflection and deserialization can also create instances, with rules that differ from an ordinary new expression. Begin with new when studying the standard lifecycle rather than assuming every construction mechanism invokes all constructors identically.

Example
class Student {
    int id;
    String name;
}
Student first = new Student();
Student second = first;
System.out.println(first.id);    // 0
System.out.println(second.name); // null
System.out.println(first == second); // true: one object, two references
3

Default Field Values versus Local Variables

Instance fields receive Java's default values before explicit instance initialization establishes the intended state. The source's uninitialized Student prints 0 and null; its Employee prints 0, 0.0, false, and null for the corresponding fields.

Local variables do not receive automatic default initialization. They must be definitely assigned before reading. Do not transfer the field-default rule to a local variable merely because both declarations use the same type.

Default Field Values versus Local Variables
Instance-field typeDefault valueExample
byte, short, int, long0An uninitialized id field
float, double0.0An uninitialized salary field
charNull character (U+0000)Not the printable digit '0'
booleanfalseAn uninitialized permanent field
Object/array referencesnullString name or int[] values
Local variablesNo automatic defaultMust be definitely assigned before use
4

Object Initialization: Fields, Blocks, and Constructor Logic

Initialization includes more than a constructor body. Fields first have defaults; explicit field initializers and instance initialization blocks run as part of construction, and constructor logic then establishes the desired state.

The source's Employee with field initializers has id 101, salary 50000.0, permanent true, and name Prakash instead of their defaults. An instance block can provide initialization shared across construction paths.

For a given class, field initializers and instance blocks execute in textual order when that class's initialization step is reached. In the document's Demo, number becomes 10 at the field declaration, then 20 in the block, then 30 in the constructor.

Example
class Demo {
    int number = 10;
    { number = 20; }
    Demo() { number = 30; }
}
System.out.println(new Demo().number); // 30
5

Constructor Execution and Constructor Overloading

A constructor has its class's name and declares no return type, not even void. It initializes a newly created instance; it is not an ordinary method callable later on an existing object.

If a class declares no constructor, the compiler supplies a default constructor, subject to the class's rules. Declaring any constructor suppresses that automatically supplied default constructor; a parameterized constructor does not automatically come with a no-argument alternative.

The source's Product overloads delegate with this(...) to one central initialization path. This avoids duplicating assignments. Normal completion of a new expression produces the reference; that is different from a constructor declaring a return value.

Example
class Product {
    int id;
    String name;
    double price;
    Product() { this(0, "Unknown", 0.0); }
    Product(int id, String name) { this(id, name, 0.0); }
    Product(int id, String name, double price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }
}
// new Product().name is "Unknown".
// new Product(102, "Phone", 25000.0).price is 25000.0.
6

Constructor Chaining and Inheritance Order

A subclass instance includes inherited state. In the source's conventional Parent/Child example, the parent field initializer runs, then the parent constructor body, then the child field initializer, then the child constructor body.

A useful model is defaults first, superclass constructor chain, current-class fields and blocks in textual order, and the remaining current constructor body. this(...) chains must also be followed; the current class's instance initializers do not repeat once per delegating constructor.

This is a simplified model for the examples, not a claim that all current Java constructors must begin with an explicit super call as their first statement. Newer Java syntax permits restricted constructor-prologue statements before explicit superclass invocation, with rules limiting access to the instance under construction.

After all fields receive defaults, parent field initializers and constructor execute, followed by child initializers and constructor, then a completed reference is returned
Conventional Parent/Child construction order used in the source example.
Example
class Parent {
    int parentValue = initializeParent();
    private int initializeParent() {
        System.out.println("Parent field initializer");
        return 10;
    }
    Parent() { System.out.println("Parent constructor"); }
}
class Child extends Parent {
    int childValue = initializeChild();
    private int initializeChild() {
        System.out.println("Child field initializer");
        return 20;
    }
    Child() { System.out.println("Child constructor"); }
}
// new Child() prints the four messages in the order shown above.
7

Constructors Should Establish a Valid State

The document's BankAccount constructor rejects a missing account number and a negative initial balance. Constructor validation is an opportunity to enforce invariants before normal use begins.

If validation throws, construction does not complete normally and the new expression does not return a successfully constructed instance. Avoid publishing this while construction is in progress; other code should not observe a partially initialized object.

A constructor should also avoid acquiring resources without a clear failure-cleanup plan. A thrown constructor exception does not automatically close every external resource already acquired.

Example
class BankAccount {
    private final String accountNumber;
    private double balance;
    BankAccount(String accountNumber, double initialBalance) {
        if (accountNumber == null || accountNumber.isBlank()) {
            throw new IllegalArgumentException("Account number is required");
        }
        if (initialBalance < 0) {
            throw new IllegalArgumentException("Initial balance cannot be negative");
        }
        this.accountNumber = accountNumber;
        this.balance = initialBalance;
    }
}
8

Object Reachability and Reference Paths

Reachability asks whether an object can still be accessed by a potential continuing computation. Collectors reason about paths from live program roots, not whether the programmer remembers using one particular variable.

Setting first = null removes one reference. If second still aliases the same object, that object remains available through second. An Employee's Address can likewise remain reachable through employee.address without any separate local Address variable.

A static field, collection entry, active computation, or another reachable object can retain a path. An unreachable object can still occupy memory until a later collection; reachability and physical reclamation are distinct facts.

A live reference reaches Employee and its Address, while a separate pair of objects referring only to one another has no live root path
One remaining live path retains an object. A disconnected cycle alone does not make its objects live.
Example
Student first = new Student();
Student second = first;
first = null;
System.out.println(second.id); // Object is still usable.

class Address { String city = "Hyderabad"; }
class Employee { Address address = new Address(); }
// A usable Employee reference can retain its Address through this field.
9

Strong, Soft, Weak, and Phantom Reachability

The reference API distinguishes several strengths of reachability. These are categories under specific rules, not a mandatory sequence that every ordinary object traverses.

Ordinary strong references are the usual way application code keeps objects alive. Soft and weak references allow different collector treatment; a weak reference does not keep its referent alive in the same way as a strong reference.

Phantom references support reference-queue-based cleanup strategies rather than ordinary access to the referent. Most objects never need soft, weak, or phantom references; choosing one requires understanding its API contract and cleanup implications.

Strong, Soft, Weak, and Phantom Reachability
CategoryMeaningPractical caveat
Strongly reachableAccessible through ordinary strong pathsOne remaining usable path can retain the object
Softly reachableNot strong, but reachable through a soft referenceMay be cleared in response to memory demand
Weakly reachableNeither strong nor soft, but reachable through a weak referenceDoes not preserve lifetime like a strong reference
Phantom reachableSpecial post-finalization reachability where applicableUsed with cleanup/reference-queue patterns, not get() access
UnreachableNo applicable reference-strength path remainsReclamation timing is still not immediate
10

Object Eligibility for Garbage Collection

An object becomes eligible for reclamation when it is no longer reachable in a way that requires preserving it, subject to applicable reference and finalization rules. Eligibility is permission to reclaim, not an immediate delete command.

A method-local object can become eligible after the method returns if it did not escape. It need not be explicitly assigned null. Conversely, a returned object or one retained in a static field, collection, or another reachable object can outlive the method that created it.

The lexical scope of a local variable is not an exact lifetime guarantee. Runtime liveness and potential continuing computation matter. Explicitly nulling every local at method end is generally unnecessary.

Mutually referencing objects with no live root path can be collected as a group. Reference cycles do not inherently prevent tracing garbage collection, unlike the simple reference-counting intuition many beginners apply.

Object Eligibility for Garbage Collection
SituationWhat changed?Collectible immediately by definition?
obj = nullOne reference removedOnly eligible if no other preserving path; no timing guarantee
Method returnsIts local execution endsOnly if the object did not escape or remain reachable
Cache.value retainedStatic reference remains usableRetained through that path
Two disconnected cyclic objectsNo live root reaches eitherCycle alone does not retain them
Resource close() calledResource protocol changedObject may remain reachable and allocated
11

System.gc() Is a Request, Not Proof of Collection

System.gc() requests a best effort at garbage collection. It does not guarantee collection of a particular object, a particular reclaimed byte count, or completion of a collection before returning.

The source prints GC was requested after clearing a reference. That output proves only that the statement executed; it does not prove that the old object has been reclaimed.

Do not use a fixed sleep after System.gc() as a correctness mechanism or test that cleanup has occurred. Collector behavior and configuration differ, and the absence of memory pressure may mean an unreachable object remains allocated for some time.

Example
Object obj = new Object();
obj = null;
System.gc();
System.out.println("GC was requested");
// No deterministic claim about this object's reclamation follows.
12

Object Finalization (Deprecated for Removal)

Legacy finalization allowed overriding Object.finalize() for cleanup associated with an object's loss of reachability. It has been deprecated for removal since Java 18. The source notes that Java SE 26 implementations may disable finalization; new code must not depend on it.

A finalizer may run late or never during the application's lifetime. System.gc() followed by Thread.sleep does not make the document's legacy finalize message guaranteed. Finalization adds collector work and can cause latency, leaks, and reliability or security problems.

The legacy mechanism can also resurrect an object by saving this in a reachable location. That makes lifecycle reasoning harder, not a useful modern cleanup strategy. Finalization is not a deterministic Java equivalent of a C++ destructor.

Example
// Legacy behavior for recognition only; do not add this to new code.
// class Demo {
//     static Demo saved;
//     @Override
//     protected void finalize() throws Throwable {
//         saved = this; // Resurrection: creates a reachable path again.
//     }
// }
13

AutoCloseable, Try-with-Resources, and Cleaner

AutoCloseable defines an explicit close contract. Try-with-resources invokes close when control leaves the managed scope, including exceptional exit after successful resource initialization.

The source's FileResource prints Using resource followed by Resource closed. A production close implementation must release the actual file, socket, connection, or native handle and follow that API's exception rules.

Cleaner can support suitable fallback cleanup associated with reachability changes. It is not a substitute for prompt explicit release; its action is not guaranteed to run at a specific time. Cleanup state must not accidentally strongly retain the object whose reachability should trigger cleanup.

AutoCloseable, Try-with-Resources, and Cleaner
ApproachWhen cleanup is requestedRecommended role
Explicit close()At an application-chosen pointFollow the resource's lifecycle contract
Try-with-resourcesOn exit from a successfully initialized resource scopePreferred scoped cleanup for AutoCloseable
CleanerAs reachability-based fallback permitsSpecialized fallback, not timing-dependent logic
finalize()Legacy, nondeterministic and possibly disabledDo not use in new code
Example
class FileResource implements AutoCloseable {
    void useResource() { System.out.println("Using resource"); }
    @Override
    public void close() { System.out.println("Resource closed"); }
}
// Inside a caller:
// try (FileResource resource = new FileResource()) {
//     resource.useResource();
// }
// close() is invoked on normal or exceptional exit from the block.
14

Object Destruction versus External Resource Release

Java has no general-purpose destructor guaranteed to execute at the moment an ordinary object becomes unreachable. Storage reclamation belongs to the collector, which determines when and how to reclaim eligible memory.

An open stream's Java object and its underlying operating-system file handle have different lifetimes. Closing the handle is important even if the wrapper object will eventually become unreachable and be collected.

The source improves a manual FileInputStream.close call to try-with-resources so cleanup is not skipped by an exception during use. Closing does not erase the reference or force reclamation; collection does not replace the explicit close protocol.

Object Destruction versus External Resource Release
What needs cleanup?ExampleCorrect mechanism
Ordinary object memoryUnused Employee instanceGarbage collection
File handleFileInputStreamclose() or try-with-resources
Database connectionJDBC connectionExplicit close/pool return according to API
Network socketOpen socketExplicit lifecycle API
Held lockExplicit lock acquisitionAppropriate unlock/release protocol
Fallback native cleanupSpecialized resource wrapperCleaner where appropriate, not prompt cleanup logic
Example
import java.io.FileInputStream;
import java.io.IOException;

class ResourceExample {
    public static void main(String[] args) throws IOException {
        try (FileInputStream input = new FileInputStream("data.txt")) {
            System.out.println(input.read());
        }
    }
}
// The file handle is closed at scope exit.
// No claim is made about when the stream object's heap memory is reclaimed.
15

Complete Example: Construction, Use, and Cleanup

The source's Employee example brings together a field initializer, instance block, constructor, ordinary use, and an explicit AutoCloseable action. It deliberately demonstrates two lifecycles without equating them.

active starts with its default false, then its initializer sets true. The instance block prints before the constructor message; display then prints the assigned id and name and active true. Exiting the managed block calls close, which sets active false and prints its message.

Employee implements AutoCloseable here only to demonstrate explicit cleanup. Ordinary Employee objects do not need that interface to be garbage-collected. close does not destroy this instance, and another retained alias would still refer to it.

Complete Example: Construction, Use, and Cleanup
StepObservable eventInterpretation
1Instance initialization blockField initializer has set active = true
2Constructor executedid and name have been assigned
3ID: 101; Name: Rahul; Active: trueObject is in use
4Employee resource closedclose() runs and changes active
5Try-with-resources block completedExplicit cleanup completed; memory reclamation is separate
Example
class Employee implements AutoCloseable {
    private int id;
    private String name;
    private boolean active = true;
    { System.out.println("Instance initialization block"); }
    Employee(int id, String name) {
        this.id = id;
        this.name = name;
        System.out.println("Constructor executed");
    }
    void display() {
        System.out.println("ID: " + id);
        System.out.println("Name: " + name);
        System.out.println("Active: " + active);
    }
    @Override
    public void close() {
        active = false;
        System.out.println("Employee resource closed");
    }
}
public class Main {
    public static void main(String[] args) {
        try (Employee employee = new Employee(101, "Rahul")) {
            employee.display();
        }
        System.out.println("Try-with-resources block completed");
    }
}
16

Quick Revision and Interview Questions

Creation requests an instance; initialization establishes its state; constructor execution is part of that initialization. Reachability determines whether program use can still access an instance, and eligibility allows later collection rather than commanding it.

For interviews, emphasize constructor order, aliases, nondeterministic reclamation, and explicit resource ownership. An accurate explanation separates a reference becoming unusable, a resource being closed, and storage being reclaimed.

Quick Revision and Interview Questions
QuestionAnswerCaveat
Does null destroy the object?No, it removes one referenceOther paths may remain
Does method return collect all its objects?NoObjects can escape through returns, fields, or collections
Can System.gc() force collection?NoIt requests best effort, not a particular result
Does a constructor return a reference?It declares no return typeThe successful new expression evaluates to a reference
Is finalize a destructor?NoIt is legacy, nondeterministic, and deprecated for removal
Does GC promptly close files?Do not rely on itUse explicit close or try-with-resources
Does AutoCloseable enable GC?NoIt supplies a resource cleanup contract
Is unreachable the same as reclaimed?NoAn unreachable object may remain allocated
Must every object pass through weak reachability?NoReference strengths are categories, not universal stages