Constructors in Java: Complete Guide

A constructor is a special method called when an object is created with new. Its job is to initialize the object's fields into a valid state before any other code touches it.

Basic constructor

public class User {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

User u = new User("Alice", 30);

Key rules

  • The name must match the class name, exactly.
  • No return type β€” not even void.
  • Cannot be static, final, or abstract.
  • Can have any access modifier: public, protected, package-private, or private.

The default constructor

If you don't declare any constructor, Java generates an implicit no-argument constructor. The moment you declare one, that freebie disappears.

public class Point {
    int x, y;
    // No constructor written β€” default public Point() is generated
}

Point p = new Point(); // works

// --- but ---
public class Point2 {
    int x, y;
    public Point2(int x, int y) { this.x = x; this.y = y; }
    // No default constructor anymore
}

Point2 p = new Point2();        // ❌ compile error
Point2 p2 = new Point2(1, 2);   // βœ…

Multiple constructors (overloading)

public class Rectangle {
    private final int width, height;

    public Rectangle() {
        this(1, 1);            // delegate to another constructor
    }

    public Rectangle(int side) {
        this(side, side);      // square
    }

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

this(...) delegates to another constructor of the same class β€” and must be the first statement in the constructor body.

Calling the parent constructor

super(...) invokes a constructor of the superclass. If you don't call it explicitly, Java inserts an implicit super(). Like this(), it must be the first line.

public class Animal {
    protected final String name;
    public Animal(String name) { this.name = name; }
}

public class Dog extends Animal {
    private final String breed;
    public Dog(String name, String breed) {
        super(name);     // call Animal(String)
        this.breed = breed;
    }
}

Copy constructor

Java doesn't have a built-in copy constructor like C++, but you can write one explicitly:

public class Point {
    int x, y;

    public Point(Point other) {
        this(other.x, other.y);
    }
    public Point(int x, int y) { this.x = x; this.y = y; }
}

Point a = new Point(3, 4);
Point b = new Point(a); // copy

For mutable objects, remember to copy nested collections defensively.

Private constructor

A private constructor prevents instantiation from outside the class β€” useful for:

  • Utility classes with only static methods (e.g. java.util.Collections)
  • Singletons
  • Factory patterns where only a named static method exposes construction
public final class Utils {
    private Utils() {
        throw new UnsupportedOperationException("No instances");
    }
    public static int sq(int n) { return n * n; }
}

Static factory methods

A named static method is often clearer than a constructor. It can cache instances, return a subtype, or fail with a meaningful method name.

public class Color {
    private final int r, g, b;
    private Color(int r, int g, int b) { this.r = r; this.g = g; this.b = b; }

    public static Color ofRgb(int r, int g, int b) {
        return new Color(r, g, b);
    }
    public static Color fromHex(String hex) {
        int v = Integer.parseInt(hex.substring(1), 16);
        return new Color((v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF);
    }
}

Color c = Color.fromHex("#FF8800");

Records: constructors for free

Since Java 16, a record auto-generates a constructor matching its components:

public record Point(int x, int y) { }

Point p = new Point(3, 4);
int x = p.x(); // accessor auto-generated too

You can add a compact constructor for validation:

public record User(String name, int age) {
    public User {
        if (age < 0) throw new IllegalArgumentException("age < 0");
        name = name.trim();
    }
}

Initialization order

When new is called, the JVM performs these steps:

  1. Allocate memory; all fields set to default values (0 / null / false).
  2. Call the parent constructor (super(...)), recursively up to Object.
  3. Run field initializers and {} initializer blocks, in the order they appear.
  4. Run the body of the chosen constructor.

Common mistakes

  • Forgetting to call super(...) when the parent has no no-arg constructor.
  • Leaking this from a constructor (e.g. registering a listener) β€” the object isn't fully built yet.
  • Calling overridable methods from a constructor β€” in subclasses, those methods see half-initialized state.
  • Doing too much work β€” constructors should set state, not load files or call remote APIs.
  • Returning null from a factory static method β€” prefer Optional or an exception.

A good constructor is short, sets every required field, rejects invalid input, and leaves the object in a fully usable state. Treat it as the contract that every instance passes through.