Classes and Objects

This section documents the current Java release line, with Java 25 LTS as the reference point (no specific patch version is pinned), as published at the Java developer portal, the Java Tutorials, and the Java SE API specification — which are the references these pages are written and verified against.

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

A class is a template that bundles state (fields) with behaviour (methods) and the code that builds an instance (constructors). This page follows the dev.java "Classes and Objects" track and the Java Tutorials on declaring classes.

Fields, Methods, and Constructors

this refers to the object the current method or constructor is running on; use it to tell a field apart from a parameter of the same name. A constructor has the class’s name and no return type. See Providing Constructors for Your Classes.

class Account {
    private final String owner;     // instance fields: one copy per object
    private long balanceCents;

    Account(String owner, long openingCents) {
        this.owner = owner;                     // this.owner is the field; owner is the parameter
        this.balanceCents = openingCents;
    }

    void deposit(long cents) {
        balanceCents += cents;                  // 'this.' is optional when unambiguous
    }

    long balanceCents() {
        return balanceCents;
    }
}

var a = new Account("Ada", 10_000);
a.deposit(2_500);
System.out.println(a.balanceCents());           // 12500

Every class implicitly extends java.lang.Object and inherits toString, equals, hashCode, and getClass from it; overriding them correctly is covered on Inheritance and Polymorphism.

Constructor chaining and the default constructor

One constructor may delegate to another in the same class with this(…​), which must be its first statement. If a class declares no constructor at all, the compiler supplies a public no-argument default constructor; writing any constructor of your own removes that gift.

class Rectangle {
    private final int width;
    private final int height;

    Rectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }

    Rectangle(int side) {
        this(side, side);           // delegates to Rectangle(int, int)
    }

    Rectangle() {
        this(1);                    // -> this(1, 1)
    }
}

Delegating to a superclass constructor uses super(…​) instead — see Inheritance and Polymorphism.

Objects, References, and null

new allocates an object on the heap and returns a reference to it. A variable of a class type holds that reference, never the object itself; assigning the variable copies the reference, so two variables can name one object.

var x = new Account("Bo", 500);
var y = x;                          // same object, two references
y.deposit(100);
System.out.println(x.balanceCents());   // 600  -- x and y observe one object

A reference that points to nothing is null. Using . through a null reference throws NullPointerException; since Java 15 (opt-in in Java 14) the message names the exact expression that was null and the operation attempted on it, which usually pinpoints the bug without a debugger.

String name = null;
// int n = name.length();          // NullPointerException: Cannot invoke "String.length()"
                                   // because "name" is null

int n = (name != null) ? name.length() : 0;
java.util.Objects.requireNonNull(name, "name");   // fail fast, with a clear message

Prefer Optional over null for "might be absent" return values, and guard constructor arguments with Objects.requireNonNull.

Garbage collection

Java has no free and no delete. The garbage collector reclaims an object automatically once it is no longer reachable from any live reference. You influence this only by dropping references — letting a local go out of scope, or clearing a long-lived field. finalize() is deprecated for removal; release external resources with try-with-resources over AutoCloseable, covered on I/O and Files.

static Members and Initializer Blocks

A static member belongs to the class, not to any instance: one copy, shared by all. Use static methods for behaviour that needs no instance state, and static final fields for constants (named in UPPER_SNAKE_CASE). See Understanding Class Members.

class Circle {
    static final double TAU = 2 * Math.PI;      // constant: one per class

    private static int created = 0;             // shared mutable counter
    private final double radius;

    Circle(double radius) {
        this.radius = radius;
        created++;
    }

    static int instancesCreated() {             // no 'this' available in a static method
        return created;
    }

    double circumference() {
        return TAU * radius;
    }
}

Initializer blocks run when the class or the object is being set up:

class Lookup {
    static final java.util.Map<String, Integer> CODES;
    private final long createdAt;

    static {                        // static initializer: runs once, when the class loads
        CODES = java.util.Map.of("OK", 200, "NOT_FOUND", 404);
    }

    {                               // instance initializer: runs before every constructor body
        createdAt = System.nanoTime();
    }

    Lookup() {
        // createdAt is already assigned here
    }
}

A static block runs once when the class is initialized; an instance block runs on every construction, just after super(…​) and before the constructor body. Most classes need neither — prefer field initializers and constructor code — but a static block is handy for building an unmodifiable constant that takes more than a single expression.

Access Modifiers at a Glance

modifier           same class   same package   subclass, other package   everywhere
public                 yes           yes                  yes                 yes
protected              yes           yes                  yes                  no
(package-private)      yes           yes                   no                  no
private                yes            no                   no                  no

Omitting the modifier means package-private. The guiding principle is encapsulation: keep fields private and expose only the methods that form the type’s contract, so invariants (such as "balance is never negative") stay enforceable. Add accessors deliberately, not reflexively — when the data really is the API, a record is a better fit (Records and Sealed Classes). The full rules, including protected access across packages and the module system, are on Packages and Modules.

JVM Memory: Stacks and the Heap

Each thread has its own stack of frames; a frame holds one method call’s parameters and local variables. A primitive local sits directly in the frame, but an object local holds only a reference into the shared heap, where new puts the object. Loaded class metadata — the Class object, method bytecode, and static fields — lives in metaspace.

Two per-thread stacks on the left, each a column of call frames whose slots hold primitives and references; a shared heap on the right holding Account and String array objects; arrows from stack reference slots into the heap objects they point to; a separate box showing class metadata loaded into metaspace

See Also