Constructors in Java

A constructor runs once when an object is created, and its job is to leave that object in a valid state.

Definition

A constructor is a special member that initialises a new object. It has the same name as the class, declares no return type, and cannot be called like an ordinary method.

Why constructors exist

Without one, an object would come into existence half built and every caller would have to remember which setters to call in which order. A constructor makes the valid state a precondition rather than a hope.

Syntax

public class Employee {

    private final String name;
    private final String department;
    private double salary;

    public Employee(String name, String department, double salary) {
        if (salary < 0) {
            throw new IllegalArgumentException("Salary must not be negative");
        }
        this.name = name;
        this.department = department;
        this.salary = salary;
    }
}

The default constructor

public class Note {
    private String title;
}

Note n = new Note();   // works: the compiler supplied a no argument constructor

If a class declares no constructor at all, the compiler adds a public no argument one. The moment you declare any constructor, that free one disappears.

public class Note {
    private String title;

    public Note(String title) {
        this.title = title;
    }
}

// Note n = new Note();   // compile error: no such constructor any more

Overloading constructors

public class Rectangle {

    private final double width;
    private final double height;

    public Rectangle(double side) {
        this(side, side);            // delegates, must be the first statement
    }

    public Rectangle(double width, double height) {
        if (width <= 0 || height <= 0) {
            throw new IllegalArgumentException("Sides must be positive");
        }
        this.width = width;
        this.height = height;
    }
}

Chaining with this(...) keeps validation and assignment in a single place. Only one such call is allowed, and it must come first.

Constructors and inheritance

class Vehicle {
    private final String registration;

    Vehicle(String registration) {
        this.registration = registration;
    }
}

class Taxi extends Vehicle {
    private final int seats;

    Taxi(String registration, int seats) {
        super(registration);      // must be the first statement
        this.seats = seats;
    }
}

A subclass constructor always runs a superclass constructor first. If you do not write super(...), the compiler inserts super(). When the superclass has no no argument constructor, the omission becomes a compile error, which is a frequent source of confusion.

Order of initialisation

  1. Static fields and static blocks, once, when the class is first loaded.
  2. The superclass constructor.
  3. Instance field initialisers and instance initialiser blocks, in source order.
  4. The body of this constructor.
public class Demo {

    static { System.out.println("1 static block"); }

    { System.out.println("3 instance block"); }

    private int value = report("2 field initialiser runs with the block, in order");

    public Demo() {
        System.out.println("4 constructor body");
    }

    private static int report(String message) {
        System.out.println(message);
        return 0;
    }
}

Private constructors

public final class MathUtils {

    private MathUtils() {
        throw new AssertionError("Utility class, do not instantiate");
    }

    public static int square(int n) {
        return n * n;
    }
}

A private constructor prevents instantiation. It is used for utility classes, for singletons, and for classes that expose static factory methods instead.

Static factory methods as an alternative

public final class Temperature {

    private final double celsius;

    private Temperature(double celsius) {
        this.celsius = celsius;
    }

    public static Temperature ofCelsius(double value) {
        return new Temperature(value);
    }

    public static Temperature ofFahrenheit(double value) {
        return new Temperature((value - 32) * 5 / 9);
    }
}
Two constructors taking a single double would be impossible to distinguish. Named factory methods solve that, can return a cached instance, and can return a subtype. They are worth reaching for whenever a constructor name would not explain itself.

Constructor compared with method

AspectConstructorMethod
NameExactly the class nameAny valid identifier
Return typeNone, not even voidRequired
CalledOnly through new, this(...) or super(...)Directly, any number of times
InheritedNoYes
Can be staticNoYes

Common mistakes

  • Writing public void Employee(...). That is a method that happens to share the class name, and the compiler will not complain until new Employee(...) fails.
  • Adding a constructor and then being surprised that new X() stops compiling.
  • Calling an overridable method from a constructor. The subclass override runs before the subclass fields are initialised.
  • Doing heavy work such as opening a connection inside a constructor.
  • Leaving an object valid only after a setter is called afterwards.

Best practices

  • Validate every argument in the constructor and fail immediately with a clear message.
  • Assign all final fields exactly once.
  • Let short constructors delegate to the fullest one.
  • Prefer a static factory method when the parameter list alone does not explain the intent.
  • Keep constructors free of side effects beyond initialisation.

Practice

  1. Write a Book class with two constructors, one delegating to the other.
  2. Why does adding Note(String title) break existing code that called new Note()?
  3. Predict the output of the initialisation order example, then run it.
  4. Give a Circle class a factory method ofDiameter and explain why a second constructor would not work.
  5. Explain the risk of calling an overridable method from a constructor, with a two class example.

Conclusion

A constructor exists to produce a valid object and nothing else. Validate there, assign the final fields there, delegate rather than duplicate, and use a named factory method when a constructor cannot explain itself.

Written by Lorens Mishra

Default administrator account created by the installer.

Continue reading

All Java notes →

Discussion

0 comments
Sign in to join the discussion.

No comments yet. Be the first to say something.