Topic 54 of 119 6. Object-Oriented Programming in Java Interactive Tutorial

Java Constructors: Object Initialization, Types, and Rules

16 mins read Last updated: 22 Aug 2026 30,112 views

Direct Answer & Core Concept

A constructor initialises a newly created object. It is invoked automatically by new, it runs before any method can touch the object, and its job is to guarantee the object leaves the constructor in a valid state. Java recognises a constructor purely by syntax: the member name must match the class name exactly, and it must declare no return type at all. Not void, not anything else. Write void Person() and you have silently created an ordinary method that nothing will ever call, while the class quietly loses its constructor. There are four types worth distinguishing. The default constructor is generated by the compiler only when you declare no constructor of your own. The no-argument constructor is one you write yourself. The parameterized constructor takes arguments. And the copy constructor takes a single argument of the same class type. The single most consequential rule is that declaring any constructor suppresses the compiler-generated default. Add a parameterized constructor and new Product() stops compiling, often far from the class that broke it. That failure is discussed in detail below, because it is the one that surprises beginners most.

Detailed Explanation & Mechanics

1) Recognising a Constructor

Main.java
Run Online
public class Product {
    private String code;
    // A CONSTRUCTOR: name matches the class, no return type
    public Product(String code) {
        this.code = code;
    } // NOT A CONSTRUCTOR: this is a method returning void
    public void Product(String code) {
    }
}

Java has no constructor keyword. The rules that identify one are structural: the name matches the class exactly, there is no return type, and it cannot be static, final, abstract, or native.

ModifierLegal on a constructor?
public, protected, privateYes, controls who may construct
staticNo, a constructor belongs to an instance
finalNo, constructors are never inherited
abstractNo, it would have nothing to initialise
synchronizedNo, object creation is not overridable

2) The Four Types

Main.java
Run Online
public class Product {
    private String code;
    private long priceInCents;
    // NO-ARGUMENT, written explicitly
    public Product() {
        this(UNSET, ZERO);
    } // PARAMETERIZED, the single place the fields are assigned
    public Product(String code, long priceInCents) {
        if (code == null || code.isBlank()) {
            throw new IllegalArgumentException("Code is required");
        } if (priceInCents < ZERO) {
            throw new IllegalArgumentException("Price cannot be negative");
        } this.code = code;
        this.priceInCents = priceInCents;
    } // COPY, taking a single argument of the same type
    public Product(Product source) {
        this(source.code, source.priceInCents);
    } // the DEFAULT constructor only appears when you declare none of the above
}

The default constructor is not a fourth thing you write. It is what the compiler generates for you when you declare no constructor at all, and it does nothing beyond leaving every field at its default value.

Writing an explicit no-argument constructor is not redundant once you have any other constructor. It is the only thing that keeps new Product() compiling, and frameworks frequently depend on being able to construct an object and then populate it.

3) Order of Execution

Several things happen in a fixed order when you write new Product(...), and knowing the sequence explains most constructor surprises.

OrderWhat happens
FirstMemory is allocated and fields get default values
SecondStatic initialisers and static blocks run, once per class
ThirdInstance initialisers and instance blocks run, in textual order
FourthThe superclass constructor body runs
LastThis constructor body runs


Sequence diagram showing constructor execution from memory allocation through superclass body to subclass body
Superclass construction always completes before a subclass constructor body begins.

The critical consequence: an instance initializer block that reads a field assigned in the constructor body sees the default value, not the assigned one, because blocks run first.

Main.java
Run Online
public class Widget {
    private final int size;
{
        // Runs BEFORE the constructor body, so size is still 0 here
        System.out.println("block sees size = " + size);
    } public Widget(int size) {
        this.size = size;
        System.out.println("constructor sees size = " + size);
    }
}

4) Chaining with this and super

When a class has many constructors, duplicating the shared initialisation in each one invites divergence. Use this(...) to delegate to another constructor in the same class.

Main.java
Run Online
public Product() {
    this("UNSET", 0L);
    // must be the FIRST statement
}public Product(String code) {
    this(code, ZERO);
    // delegates again, still first
}public Product(String code, long priceInCents) {
    // the real initialisation happens here only
}

this(...) and super(...) must each appear as the first statement of a constructor, and you may use one or the other, never both. Passing arguments to the parent goes through super(...) as shown in the next section. Full detail is in constructor chaining.

5) Calling a Parent Constructor

Constructors are not inherited. A subclass always runs a parent constructor first, and if you omit super() the compiler inserts a call to the parent no-argument constructor.

Main.java
Run Online
public class PhysicalProduct extends Product {
    private final double weightInKg;
    // super() is inserted automatically, and must reach a no-arg parent
    public PhysicalProduct() {
        this(UNSET, ZERO, ZERO_DOUBLE);
    } public PhysicalProduct(String code, long priceInCents, double weightInKg) {
        super(code, priceInCents);
        // must be the FIRST statement
        if (weightInKg < ZERO) {
            throw new IllegalArgumentException("Weight cannot be negative");
        } this.weightInKg = weightInKg;
    }
}

If the parent declares only parameterized constructors, omitting super(...) is a compile error, because there is no no-argument parent constructor for the compiler to insert. This is the most common inheritance error in Java.

6) The Default Constructor Trap

This deserves its own section because it breaks code far from where it was introduced. Declare any constructor and the compiler stops generating the default one.

Main.java
Run Online
// Stage one: this compiles, because the default constructor exists
public class Session {
    private final String userId;
    public Session(String userId) {
        this.userId = userId;
    } public String getUserId() {
        return userId;
    }
}// Somewhere else, far from Session
Session guest = new Session();
// COMPILE ERROR

Adding the parameterized constructor did not change any existing line. It removed a constructor that other code depended on, and the error surfaces in a file that never mentions the change.

Declare an explicit no-argument constructor the moment you add a parameterized one, unless you intend to prevent no-arg construction. Failing that, declare the class final or give the single constructor a deliberate design reason.

7) Final Fields Must Be Assigned

A final field can be assigned exactly once, and the compiler checks it. A constructor that leaves a final field unassigned does not compile, which is the compiler enforcing your class invariant.

Main.java
Run Online
public class Session {
    private final String userId;
    // must be assigned in every constructor
    private final long createdAtMillis;
    public Session(String userId) {
        this.userId = userId;
        this.createdAtMillis = System.currentTimeMillis();
    }
}

Chaining through this(...) means only the constructor that actually assigns the fields needs the assignment, since the delegating one never reaches its own body.

8) Constructors and Overloading

Constructors overload by parameter list, exactly like methods, and differ only in that they share the class name and have no return type.

Main.java
Run Online
public Report() {
    this(UNTITLED, ZERO);
}public Report(String title) {
    this(title, ZERO);
}public Report(String title, long rowCount) {
    this.title = title;
    this.rowCount = rowCount;
}

Constructors are not overloaded by access modifier or by static. Two constructors differing only in accessibility would have the same erasure and the same name, so the compiler rejects it.

9) Common Misconceptions

ClaimVerdict
A constructor is a method with a special nameFalse. It has no return type, so it is not a method.
A constructor must return the objectFalse.new yields the reference, not the constructor.
The default constructor always existsFalse. Only when you declare no constructor.
Constructors are inheritedFalse. Each class declares its own.
A constructor can be static or finalFalse. Both are compile errors.
Overriding a parent constructor is allowedFalse. Constructors are never overridden.
You may call super after other statementsFalse. It must be the first statement.
Instance blocks run after the constructor bodyFalse. They run before it.

Frequently Asked Questions

Why does a constructor not have a return type?


Because Java identifies constructors by structure rather than by a keyword. A member whose name matches the class and which declares no return type is a constructor, invoked by new. Adding void, even once, converts it into an ordinary method returning nothing, and the class then silently has no constructor with that signature.

Can a constructor be static, final, or abstract?


No, all three are compile errors. A constructor belongs to a specific object being created, so static makes no sense. It is never inherited, so final is meaningless. And an abstract constructor would have nothing to initialise, since an abstract class cannot be instantiated anyway.

What happens if I declare one parameterized constructor?


The compiler stops generating a default constructor, so new ClassName() no longer compiles. This frequently breaks code in a different file that never mentioned your change, which is why declaring an explicit no-argument constructor alongside your parameterized ones is a common defensive habit.

Are constructors inherited by subclasses?


No. Every class declares its own constructors. A subclass must invoke a parent constructor explicitly with super, or the compiler inserts a call to the parent no-argument constructor. If the parent has only parameterized constructors, omitting super is a compile error.

When do instance initializer blocks run?


After fields are given their default values but before the constructor body runs, in textual order alongside field initialisers. So a block that reads a field sees the default value, not one assigned in the constructor, which is why assigning fields inside a block is almost always a mistake.

What is the difference between a default and a no-argument constructor?


The default constructor is generated by the compiler and exists only when you declare no constructor, doing nothing beyond leaving fields at their defaults. A no-argument constructor is one you write explicitly, and writing it deliberately lets you validate state or provide sensible defaults that the generated one would not.

Can I call this and super in the same constructor?


No. Because both must be the first statement, only one can occupy that position. Use this to delegate to another constructor of the same class, and super to invoke a parent constructor, and pick the one that matches what you need.

Do constructors get overloaded by return type or access modifier?


They cannot be overloaded by return type, because constructors have none, and they cannot differ only by access modifier, since that would give them the same signature. Overloading a constructor works by varying the parameter list, exactly as it does for methods.

Runnable Code Example: Java Constructors

Java 17+
Main.java
Run Online
// Every rule about constructors, demonstrated in one compilable program.
public class ConstructorsDemo {
    private static final long ZERO = 0L;
    private static final double ZERO_DOUBLE = 0.0;
    private static final String UNSET = "UNSET";
    // THE DEFAULT CONSTRUCTOR IS COMPILER GENERATED, so it only exists
    // in the Stages class below, which declares no constructor at all.
    // A CONSTRUCTOR IS NOT A METHOD: no return type, name matches the class
    public static class Product {
        private final String code;
        private final long priceInCents;
        // NO-ARGUMENT, delegating to the parameterized constructor
        public Product() {
            this(UNSET, ZERO);
        } // PARAMETERIZED, the single place the fields are assigned
        public Product(String code, long priceInCents) {
            if (code == null || code.isBlank()) {
                throw new IllegalArgumentException("Code is required");
            } if (priceInCents < ZERO) {
                throw new IllegalArgumentException("Price cannot be negative");
            } this.code = code;
            this.priceInCents = priceInCents;
        } // COPY CONSTRUCTOR, one argument of the same type
        public Product(Product source) {
            this(source.code, source.priceInCents);
        } // THIS IS A METHOD, not a constructor, and nothing will call it
        public void Product(String ignored) {
            throw new UnsupportedOperationException("Not a constructor");
        } public String getCode() {
            return code;
        } public long getPriceInCents() {
            return priceInCents;
        } @Override public String toString() {
            return "Product[" + code + " @ " + priceInCents + "]";
        }
    } // CONSTRUCTORS ARE NOT INHERITED, so this declares its own
    public static class PhysicalProduct extends Product {
        private final double weightInKg;
        // super() would be inserted automatically here
        public PhysicalProduct() {
            this(UNSET, ZERO, ZERO_DOUBLE);
        } public PhysicalProduct(String code, long priceInCents, double weightInKg) {
            // super MUST be the first statement, and is never optional here
            super(code, priceInCents);
            if (weightInKg < ZERO) {
                throw new IllegalArgumentException("Weight cannot be negative");
            } this.weightInKg = weightInKg;
        } public double getWeightInKg() {
            return weightInKg;
        }
    } // NO CONSTRUCTOR IS DECLARED, so the compiler generates the default one.
    // That default constructor disappears the moment you declare your own.
    public static class Stages {
        private final String name;
        public String getName() {
            return name;
        } // PROVES THE DEFAULT CONSTRUCTOR EXISTS FOR THIS CLASS ONLY
        public static Stages createDefault() {
            return new Stages();
        }
    } // BLOCKS RUN BEFORE THE CONSTRUCTOR BODY, so ordering matters
    public static class Widget {
        private static int instancesCreated;
        private final int size;
        private final String log;
        static {
            instancesCreated = 0;
        } {
            // size is still 0 here, because field assignment happens after
            log = "block saw size " + size;
        } public Widget(int size) {
            if (size <= ZERO) {
                throw new IllegalArgumentException("Size must be positive");
            } this.size = size;
            instancesCreated++;
        } public int getSize() {
            return size;
        } public String getLog() {
            return log;
        } public static int getInstancesCreated() {
            return instancesCreated;
        }
    } // A FINAL FIELD MUST BE ASSIGNED IN EVERY CONSTRUCTOR THAT DOES NOT CHAIN
    public static class Session {
        private final String userId;
        private final long createdAtMillis;
        public Session(String userId) {
            if (userId == null || userId.isBlank()) {
                throw new IllegalArgumentException("User id is required");
            } this.userId = userId;
            this.createdAtMillis = System.currentTimeMillis();
        } public String getUserId() {
            return userId;
        }
    } public static void main(String[] args) {
        // THE FOUR WAYS TO CONSTRUCT
        Product viaNoArg = new Product();
        Product viaArgs = new Product("LAPTOP-01", 129_999L);
        Product viaCopy = new Product(viaArgs);
        System.out.println(viaNoArg);
        System.out.println(viaArgs);
        System.out.println("copy equals source: " + viaCopy.getCode().equals(viaArgs.getCode()));
        // SUPERCLASS CONSTRUCTION ALWAYS COMPLETES FIRST
        PhysicalProduct laptop = new PhysicalProduct("PHYS-01", 199_999L, 2.5);
        System.out.println(laptop + " weighing " + laptop.getWeightInKg() + " kg");
        // THE DEFAULT CONSTRUCTOR
        Stages defaultBuilt = Stages.createDefault();
        System.out.println("default constructed name is: " + defaultBuilt.getName());
        // BLOCKS RUN BEFORE THE CONSTRUCTOR BODY
        Widget widget = new Widget(42);
        System.out.println(widget.getLog());
        System.out.println("widget size is " + widget.getSize());
        System.out.println("instances created: " + Widget.getInstancesCreated());
        Session session = new Session("user-9001");
        System.out.println("session for " + session.getUserId());
        /*
            ACTUAL OUTPUT:

                Product[UNSET @ 0]
                Product[LAPTOP-01 @ 129999]
                copy equals source: true
                Product[PHYS-01 @ 199999] weighing 2.5 kg
                default constructed name is: null
                block saw size 0
                widget size is 42
                instances created: 1
                session for user-9001

            THE NINE RULES TO REMEMBER
            1) A CONSTRUCTOR HAS NO RETURN TYPE. Not void, not anything.
               A member named like the class with no return type is a
               constructor, so adding void turns it into a dead method and
               the class quietly loses that constructor.
            2) THE NAME MUST MATCH THE CLASS EXACTLY, and it is
               case-sensitive, because there is no keyword to mark it.
            3) STATIC, FINAL, ABSTRACT, AND SYNCHRONIZED ARE ALL ILLEGAL.
               A constructor belongs to one object being created, is never
               inherited, and has nothing to override.
            4) THERE ARE FOUR TYPES. The default constructor is generated by
               the compiler, a no-argument one is written by you, a
               parameterized one takes arguments, and a copy constructor
               takes a single argument of the same type.
            5) DECLARING ANY CONSTRUCTOR REMOVES THE DEFAULT ONE, which
               breaks new ClassName() in some other file that never
               mentioned your change.
            6) EXECUTION ORDER IS FIXED. Memory, then static init, then
               instance init in textual order, then the superclass body,
               then this constructor body.
            7) THIS AND SUPER MUST BE THE FIRST STATEMENT, and you may use
               only one of them per constructor.
            8) CONSTRUCTORS ARE NOT INHERITED. Every class declares its
               own, and if the parent has no no-argument constructor you
               must call super explicitly or fail to compile.
            9) FINAL FIELDS MUST BE ASSIGNED, and chaining through this
               means only the constructor that reaches its own body needs
               to do the assigning.
        */
    }
}

Common Java Technical Interview Questions & Answers

Interview Prep
Q1: Why does a constructor not declare a return type?\nA1: Because Java identifies constructors structurally rather than with a keyword. A member whose name matches the class and which declares no return type is a constructor invoked by new. Adding void, even once, makes it an ordinary method returning nothing, and the class then silently has no constructor with that signature.\nQ2: Can a constructor be declared static, final, or abstract?\nA2: No, all three are compile errors. A constructor belongs to one specific object being created, so static makes no sense. It is never inherited, so final is meaningless. An abstract constructor would have nothing to initialise, and an abstract class cannot be instantiated anyway.\nQ3: What happens if I declare a parameterized constructor without a no-argument one?\nA3: The compiler stops generating a default constructor, so new ClassName() no longer compiles. This commonly breaks code in a completely different file that never referenced the change, which is why an explicit no-argument constructor alongside parameterized ones is a common defensive habit.\nQ4: Are constructors inherited by subclasses?\nA4: No. Every class declares its own constructors, and they are never overridden. A subclass must invoke a parent constructor with super, or the compiler inserts a call to the parent no-argument constructor. If the parent declares only parameterized constructors, omitting super is a compile error.\nQ5: When do instance initializer blocks run relative to the constructor body?\nA5: After fields receive their default values but before the constructor body executes, in textual order alongside field initialisers. A block that reads a field therefore sees the default value rather than one assigned in the constructor, which is why assigning fields inside a block is almost always a mistake.\nQ6: What is the difference between a default and a no-argument constructor?\nA6: The default constructor is generated by the compiler and exists only when you declare no constructor at all, doing nothing beyond leaving fields at their default values. A no-argument constructor is written explicitly, and writing one lets you validate state or supply sensible defaults that the generated version would not.\nQ7: Can a constructor call both this and super?\nA7: No. Both must be the first statement of the constructor body, so only one can occupy that position. Use this to delegate to another constructor of the same class and super to invoke a parent constructor, and choose whichever matches the intent.\nQ8: Can two constructors differ only by their access modifier or return type?\nA8: No. Constructors declare no return type, and two signatures differing only in accessibility are the same signature, so the compiler rejects it. Constructor overloading works by varying the parameter list, exactly as method overloading does.

Key Takeaways, Best Practices & Clean Code Rules

Java Best Practice & Production Rules

Never declare a return type on a constructor, not even void.\nKeep every constructor cheap, since it runs on every new and is a hot path in large systems.\nValidate arguments inside the constructor so an invalid object can never exist.\nDelegate shared initialisation to one constructor with this rather than repeating it.\nChain with this first and super first, never both, and always as the first statement.\nDeclare every field final wherever the value cannot change after construction.\nPrefer long cents over double for money, since floating point drifts under repeated addition.\nAvoid calling overridable methods from a constructor, since subclasses see uninitialised state.\nDo not depend on instance initializer blocks for logic, since they run before the constructor body.\nKeep constructors overloadable by logical argument groups rather than long parameter lists.\nAdd an explicit no-argument constructor alongside parameterized ones unless you intend otherwise.\nDo not let constructors perform I/O, start threads, or call remote services.\nRemember that declaring any constructor removes the compiler default.\nGive subclasses a parent with a no-argument constructor where practical, to spare them an explicit super call.\nTreat the constructor as the place where invariants are established, not as a place for setup logic.
We value your privacy & transparency

OperateTools uses cookies and local storage to ensure basic tool functionality, preserve preferences, and analyze aggregated traffic. We never sell your personal data or index your document files. Learn more in our Privacy Policy.