Constructors in Java

  • Last Updated: April 20, 2025
  • By: javahandson
  • Series
img

Constructors in Java



Learn constructors in Java the easy way: default, parameterized and copy constructors, overloading, chaining with this() and super(), initialization order, and private constructors.

1. Introduction

Constructors in Java set up an object the moment it comes to life. You write new Student(), and a constructor runs before you touch a single field.

Think about buying a new phone. The shop does not hand you an empty shell. Someone puts the battery in, installs the software, and prints your name on the box. Only then does the phone reach you, ready to use.

A constructor plays that role for your objects. It fills in the fields, checks the values, and hands back something safe to work with.

Skip that step and trouble follows. An object with a null name or a zero balance can travel deep into your code before anyone notices. By then the real cause sits ten methods away.

So constructors are not just ceremony. They decide whether an object can ever exist in a broken state.

1.1 What This Article Covers

We begin with the plain idea, then work up to the parts that trip people in interviews. Here is the plan:

  • What a constructor does, and how it differs from a method
  • No-argument, parameterized, and copy constructors
  • Shallow copy versus deep copy, with a bug you can see
  • Constructor overloading and its rules
  • Chaining with this() and super()
  • The exact order Java initializes everything
  • Static block versus constructor, side by side
  • Private constructors, singletons, enums, and records
  • Common mistakes, a full walkthrough, and interview questions

A little Java is enough to follow along. If you have created an object with new, you are ready. Every idea arrives with a short program you can run.

2. What Is a Constructor?

2.1 The Core Idea

A constructor is a special block of code that shares its name with the class. Java runs it automatically whenever you create an object.

Two rules make it recognisable at a glance. The name matches the class exactly, capital letters included. No return type appears, not even void.

public class Student {
    private String name;

    public Student(String name) {   // constructor: same name, no return type
        this.name = name;
    }

    public static void main(String[] args) {
        Student s = new Student("Suraj");   // constructor runs right here
        System.out.println(s.name);         // Output: Suraj
    }
}

Notice what the new keyword really does. It asks the JVM for memory, then hands control to the constructor to fill that memory in.

2.2 Constructor Versus Method

Beginners often ask why a constructor cannot just be a normal method. The differences are small on screen but large in behaviour.

PointConstructorMethod
NameSame as the classAny valid name
Return typeNone at allRequired, even void
Who calls itJava, during newYou, by name
How oftenOnce per objectAs often as you like
InheritanceA subclass never inherits itA subclass inherits it
OverridingImpossibleAllowed
OverloadingAllowedAllowed

That “no return type” line hides a nasty trap. Add void in front of your constructor and it silently turns into an ordinary method. We come back to that trap in section 10.

2.3 Why Constructors Matter

  • Every object starts with sensible values, never half-empty ones.
  • Required data arrives up front, because the caller must pass it.
  • Validation lives in one place, so bad input never becomes an object.
  • Callers write one clean line instead of five setter calls.

Compare two styles for a moment. With setters, a caller might forget one and ship a broken object. With a constructor, the compiler itself demands the missing value.

3. Types of Constructors in Java

Java developers usually name three kinds. Two come from the language, and one you write by hand.

3.1 The No-Argument Constructor

A no-argument constructor takes an empty parameter list. You write it when every new object should start from the same known values.

public class Student {
    private String name;
    private int rollNumber;

    public Student() {              // no-argument constructor
        this.name = "Unknown";
        this.rollNumber = 0;
    }

    public static void main(String[] args) {
        Student s = new Student();
        System.out.println(s.name + " / " + s.rollNumber); // Output: Unknown / 0
    }
}

Frameworks love this shape. Jackson, Hibernate, and many others create objects reflectively, so they often need a no-argument constructor to exist.

3.2 The Compiler’s Default Constructor

Here is a distinction most tutorials blur. The constructor you write by hand with no parameters is a no-argument constructor. The default constructor is the one the compiler adds for you.

That gift arrives on one condition: your class declares no constructor at all. Write even a single constructor of your own, and the compiler stops generating it.

public class Student {
    private String name;      // no constructor anywhere in this class
    private int rollNumber;

    public static void main(String[] args) {
        Student s = new Student();      // works, thanks to the default constructor
        System.out.println(s.name);       // Output: null
        System.out.println(s.rollNumber); // Output: 0
    }
}

Look at that output. The compiler’s default constructor initialises nothing itself. It merely calls super(), and the JVM leaves each field at its type’s zero value.

Data typeDefault value
byte, short, int, long0
float, double0.0
charthe null character (Unicode code point zero)
booleanfalse
String or any objectnull

One more detail earns you points in an interview. The default constructor copies the access level of the class. A public class gets a public one, and a package-private class gets a package-private one.

3.3 The Parameterized Constructor

A parameterized constructor accepts arguments, so each object can start with its own values. This form dominates real code.

public class Student {
    private String name;
    private int rollNumber;

    public Student(String name, int rollNumber) {
        this.name = name;
        this.rollNumber = rollNumber;
    }

    public static void main(String[] args) {
        Student a = new Student("Shweta", 102);
        Student b = new Student("Suraj", 101);
        System.out.println(a.name + " " + a.rollNumber); // Output: Shweta 102
        System.out.println(b.name + " " + b.rollNumber); // Output: Suraj 101
    }
}

Spot the this keyword on the left of each assignment. The parameter and the field share a name, so this.name means the field while plain name means the parameter.

Our guide on the this and super keywords in Java covers that shadowing rule in depth.

3.4 The Copy Constructor

A copy constructor takes another object of the same class and duplicates its values. Java has no built-in version like C++, so you write it yourself.

public class Student {
    private String name;
    private int rollNumber;

    public Student(String name, int rollNumber) {
        this.name = name;
        this.rollNumber = rollNumber;
    }

    public Student(Student other) {        // copy constructor
        this.name = other.name;
        this.rollNumber = other.rollNumber;
    }

    public static void main(String[] args) {
        Student original = new Student("Shweta", 102);
        Student copy = new Student(original);
        System.out.println(copy.name + " " + copy.rollNumber); // Output: Shweta 102
        System.out.println(original == copy);                  // Output: false
    }
}

That last line matters. The two variables point at two separate objects, so changing one never disturbs the other.

3.5 Shallow Copy Versus Deep Copy

Now a subtle bug that bites even experienced developers. The copy constructor above copies each field’s value. For an object field, that value happens to be a reference.

So both objects end up pointing at the same inner object. We call that a shallow copy, and the demo below shows the damage.

import java.util.ArrayList;
import java.util.List;

public class Student {
    private String name;
    private List<String> subjects;

    public Student(String name, List<String> subjects) {
        this.name = name;
        this.subjects = subjects;
    }

    public Student(Student other) {
        this.name = other.name;
        this.subjects = other.subjects;      // shallow: shares the same list
    }

    public static void main(String[] args) {
        List<String> list = new ArrayList<>(List.of("Math"));
        Student original = new Student("Shweta", list);
        Student copy = new Student(original);

        copy.subjects.add("Science");          // touching the copy...
        System.out.println(original.subjects); // Output: [Math, Science]
    }
}

Adding a subject to the copy changed the original. Nobody wants that surprise at 2 a.m.

The fix takes one line. Build a fresh list inside the copy constructor instead of sharing the old reference.

public Student(Student other) {
    this.name = other.name;
    this.subjects = new ArrayList<>(other.subjects);   // deep: its own list
}
// Now original.subjects prints [Math] after copy.subjects.add("Science")

Strings need no such care. A String never changes, so sharing the reference stays perfectly safe. Only mutable fields demand a deep copy.

4. Constructor Overloading

4.1 One Class, Many Doors

A class may declare several constructors, as long as their parameter lists differ. We call that constructor overloading.

Why bother? Because callers arrive with different amounts of information. Some know the name only. Others know the name and the roll number.

public class Student {
    private String name;
    private int rollNumber;

    public Student() {
        this("Unknown", 0);              // calls the two-argument version
    }

    public Student(String name) {
        this(name, 0);                   // calls the two-argument version
    }

    public Student(String name, int rollNumber) {
        this.name = name;
        this.rollNumber = rollNumber;
    }

    public static void main(String[] args) {
        System.out.println(new Student().name);              // Output: Unknown
        System.out.println(new Student("Suraj").rollNumber); // Output: 0
    }
}

Look how the first two constructors delegate to the third. Only one constructor actually assigns fields, so the real logic lives in a single place.

4.2 The Rules of Overloading

  • Every constructor carries the class name, so only the parameters distinguish them.
  • Parameter lists must differ in number, type, or order.
  • Renaming a parameter changes nothing, since the compiler ignores names.
  • Access modifiers may differ freely between the overloads.

That third bullet catches people out. Two constructors taking a single String will never compile, however different the parameter names look.

5. Constructor Chaining

Constructor chaining means one constructor calls another. Java offers two forms, and each targets a different class.

5.1 Chaining With this()

The this() call jumps to another constructor in the same class. Section 4 already used it to funnel every path into one place.

public class Student {
    private String name;
    private int rollNumber;

    public Student(String name) {
        this(name, 101);          // must be the very first statement
        System.out.println("One-arg constructor finished");
    }

    public Student(String name, int rollNumber) {
        this.name = name;
        this.rollNumber = rollNumber;
        System.out.println("Two-arg constructor finished");
    }

    public static void main(String[] args) {
        new Student("Suraj");
    }
}
// Output:
// Two-arg constructor finished
// One-arg constructor finished

Read that output carefully. The target constructor finishes first, then control returns to the caller. Chaining runs inside out, much like nested boxes.

5.2 Chaining With super()

The super() call runs a constructor of the parent class. A subclass uses it to hand the parent whatever the parent needs.

class Person {
    protected String name;

    Person(String name) {
        this.name = name;
        System.out.println("Person constructor");
    }
}

public class Student extends Person {
    private int rollNumber;

    public Student(String name, int rollNumber) {
        super(name);                 // parent builds its part first
        this.rollNumber = rollNumber;
        System.out.println("Student constructor");
    }

    public static void main(String[] args) {
        Student s = new Student("Suraj", 101);
        System.out.println(s.name + " " + s.rollNumber);
    }
}
// Output:
// Person constructor
// Student constructor
// Suraj 101

The order makes sense once you picture it. A child object contains the parent’s fields, so those fields must exist before the child touches anything. For more on that relationship, see understanding inheritance in Java.

5.3 The First-Statement Rule

Both calls follow one strict rule. A this() or super() call must sit as the first statement of the constructor.

  • Only one of the two may appear, never both together.
  • Placing either one on line two triggers a compile error.
  • Recursive chains, where two constructors call each other, also fail to compile.

Java 25 relaxed this rule through JEP 513, flexible constructor bodies. You may now run validation statements before the super() call, as long as you do not touch the object under construction. Our article on flexible constructor bodies walks through the change.

5.4 The Hidden super() Call

What happens when you write no super() at all? The compiler quietly inserts super() with no arguments as the first line.

Most of the time nobody notices. The trouble starts when the parent declares only a parameterized constructor.

class Person {
    Person(String name) { }        // no no-argument constructor here
}

class Student extends Person {
    Student() {
        // compiler inserts super(); -> error: no such constructor in Person
    }
}

Two fixes exist. Call super("something") explicitly in the child, or add a no-argument constructor to the parent. Pick whichever suits your design.

6. The Order of Initialization

6.1 The Full Sequence

Interviewers adore this question. Several things run when you create an object, and the sequence never varies.

  • The JVM loads the class once, running static fields and static blocks in source order.
  • A parent class always finishes its static work before the child starts.
  • On new, the JVM allocates memory and sets every field to its zero value.
  • The constructor begins with this() or super(), explicit or inserted.
  • After super() returns, instance field initialisers and instance blocks run in source order.
  • Finally the rest of the constructor body runs.

Three words summarise it: static first, then parent, then child.

6.2 A Program That Proves It

Numbered print statements make the sequence obvious. Run this and watch the order.

class Parent {
    static { System.out.println("1. Parent static block"); }
    { System.out.println("3. Parent instance block"); }
    Parent() { System.out.println("4. Parent constructor"); }
}

public class Child extends Parent {
    static { System.out.println("2. Child static block"); }
    { System.out.println("5. Child instance block"); }
    Child() { System.out.println("6. Child constructor"); }

    public static void main(String[] args) {
        new Child();
    }
}
// Output:
// 1. Parent static block
// 2. Child static block
// 3. Parent instance block
// 4. Parent constructor
// 5. Child instance block
// 6. Child constructor

Notice where the parent instance block sits. It runs after super() starts but before the parent constructor body, not before the whole chain.

Create a second Child and only steps three through six repeat. Static blocks fire once per class, no matter how many objects follow.

7. Static Block vs Constructor

7.1 Side by Side

These two look similar in a file, yet they serve different masters. The table sorts them out.

PointStatic BlockConstructor
Belongs toThe classEach object
InitialisesStatic variablesInstance variables
RunsOnce, at class loadingOnce per new
Syntaxstatic { }Class name, no return type
ParametersNeverAny number
this and superUnavailableAvailable
OverloadingNot a thingFully supported

7.2 Counting the Calls

Two counters settle the argument. One sits in a static block, the other in a constructor.

public class Student {
    private static int staticCount;
    private static int objectCount;

    static {
        staticCount++;
    }

    public Student() {
        objectCount++;
    }

    public static void main(String[] args) {
        new Student();
        new Student();
        new Student();
        System.out.println("static block ran: " + staticCount); // Output: static block ran: 1
        System.out.println("constructor ran: " + objectCount);  // Output: constructor ran: 3
    }
}

Three objects, three constructor calls, one static block. That single line captures the whole difference. Our guide on the static keyword in Java explores static blocks further.

8. Rules Every Constructor Follows

8.1 The Name and Return Type Rules

Two rules define a constructor, and both are absolute.

  • The name must match the class exactly, including capital letters.
  • No return type may appear, not even void.
  • Any access modifier works: public, protected, private, or none.
  • A throws clause is perfectly legal, so a constructor may declare exceptions.

That last point surprises many people. A constructor that validates input can throw IllegalArgumentException and stop a bad object from existing.

8.2 What You Cannot Write

public class Student {
    static Student() { }      // error: constructors belong to objects
    final Student() { }       // error: nothing can override one anyway
    abstract Student() { }    // error: a constructor always has a body
}

Each error has a reason worth remembering.

  • A static constructor makes no sense, because the whole job is building one object.
  • The final keyword blocks overriding, and nobody can override a constructor.
  • An abstract constructor would carry no body, yet a constructor must always run something.

8.3 Overloaded Yes, Overridden No

Overloading works because the parameter lists differ. Overriding fails for a simpler reason: a subclass never inherits its parent’s constructors.

The subclass can only call one through super(). Its own constructor carries its own name, so no override relationship can exist.

9. Private Constructors and Special Cases

9.1 The Singleton Pattern

Mark a constructor private and nobody outside the class can call new. That restriction powers the singleton pattern, where exactly one object may exist.

public class Config {
    private static Config instance;

    private Config() { }                // nobody outside can call new Config()

    public static Config getInstance() {
        if (instance == null) {
            instance = new Config();    // the class creates it internally
        }
        return instance;
    }
}

The class keeps the only key to its own door. Callers ask getInstance() and always receive the same object.

One caution belongs here. This simple version can create two objects if several threads call getInstance() at once. Real projects add synchronisation or, more often, use an enum instead.

9.2 The Utility Class

Some classes hold nothing but static helpers. Creating an object of one would serve no purpose at all.

public final class MathUtils {

    private MathUtils() {               // stops new MathUtils()
        throw new AssertionError("No instances, please");
    }

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

Java’s own java.lang.Math uses this exact trick. A private constructor documents the intent far better than a comment does.

9.3 Constructors in an Abstract Class

An abstract class can declare constructors, which confuses almost everyone at first. Nobody can write new on it, so what runs them?

A subclass does, through super(). The abstract constructor initialises the shared fields while the subclass handles its own.

abstract class Shape {
    protected final String name;

    Shape(String name) {          // runs when a subclass object is built
        this.name = name;
    }

    abstract double area();
}

class Circle extends Shape {
    private final double radius;

    Circle(double radius) {
        super("Circle");
        this.radius = radius;
    }

    @Override
    double area() { return Math.PI * radius * radius; }
}

9.4 Enum Constructors

An enum may declare a constructor too, and it stays private whether you type the keyword or not. Java runs it once for each constant.

enum Planet {
    EARTH(6371), MARS(3389);        // each constant calls the constructor

    private final int radiusKm;

    Planet(int radiusKm) {          // implicitly private
        this.radiusKm = radiusKm;
    }

    public int radius() { return radiusKm; }
}

public class Main {
    public static void main(String[] args) {
        System.out.println(Planet.MARS.radius()); // Output: 3389
    }
}

Our article on enum in Java shows more of what enums can carry.

9.5 Record Constructors

A record generates a canonical constructor from its header, so you rarely write one. When you need validation, a compact constructor keeps things short.

record Student(String name, int rollNumber) {

    Student {                       // compact constructor, no parameter list
        if (rollNumber <= 0) {
            throw new IllegalArgumentException("Roll number must be positive");
        }
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println(new Student("Suraj", 101)); // Output: Student[name=Suraj, rollNumber=101]
        new Student("Bad", -5);                        // throws IllegalArgumentException
    }
}

The compact form skips the parameter list and the field assignments. Java performs those assignments for you after your validation passes.

10. Common Mistakes and Pitfalls

10.1 Losing the No-Arg Constructor

Add your first parameterized constructor, and the compiler’s default one vanishes. Any old code calling new Student() breaks immediately.

Frameworks feel this hardest, since many create objects reflectively through a no-argument constructor. When one is required, declare it yourself.

10.2 Adding a Return Type by Accident

public class Student {
    private String name;

    public void Student(String name) {   // void makes this a METHOD
        this.name = name;
    }
}
// new Student("Suraj") now fails: no matching constructor

This one hurts because nothing looks wrong. The compiler sees a method named Student, so your class quietly falls back to the default constructor.

Delete the void and everything works. Watch for it whenever a constructor “never runs”.

10.3 Forgetting this on a Shadowed Field

public Student(String name) {
    name = name;          // assigns the parameter to itself, field stays null
}

public Student(String name) {
    this.name = name;     // correct
}

The broken version compiles happily and prints null later. Most IDEs warn about a self-assignment, so take that warning seriously.

10.4 Calling an Overridable Method in a Constructor

Call a public, non-final method from a parent constructor and a subclass may override it. That override then runs before the subclass fields hold any value.

class Parent {
    Parent() { show(); }              // dangerous call
    void show() { }
}

class Child extends Parent {
    private String text = "hello";

    @Override
    void show() { System.out.println(text); }

    public static void main(String[] args) {
        new Child();   // Output: null, not hello
    }
}

The parent constructor finishes before text gets its value, so the override sees null. Keep constructors free of overridable calls, or mark such methods final.

10.5 Writing a Huge Constructor

A constructor with eight parameters invites mistakes. Swap two of the same type and the compiler stays silent while your data lands in the wrong fields.

Keep constructors short and focused on assignment. When a class truly needs many values, a builder reads far better at the call site.

11. A Practical Walkthrough

11.1 The Class We Need

Let us build a small BankAccount class. It pulls together overloading, chaining, validation, and a copy constructor in one file.

The rules are simple. Every account needs an owner. The opening balance may be omitted, but it must never go negative.

public class BankAccount {
    private static int accountCounter;    // shared across all accounts

    private final int accountNumber;
    private final String owner;
    private double balance;

    // 1. Owner only: opens with a zero balance
    public BankAccount(String owner) {
        this(owner, 0.0);
    }

    // 2. The main constructor, where all the work happens
    public BankAccount(String owner, double balance) {
        if (owner == null || owner.isBlank()) {
            throw new IllegalArgumentException("Owner is mandatory");
        }
        if (balance < 0) {
            throw new IllegalArgumentException("Balance cannot be negative");
        }
        this.accountNumber = ++accountCounter;
        this.owner = owner;
        this.balance = balance;
    }

    // 3. Copy constructor: same owner and balance, brand new number
    public BankAccount(BankAccount other) {
        this(other.owner, other.balance);
    }

    @Override
    public String toString() {
        return accountNumber + " | " + owner + " | " + balance;
    }
}

Study how the three constructors cooperate. Numbers one and three both delegate to number two, so validation happens exactly once.

The accountNumber field carries final, which means a constructor must set it and nothing may change it later. That single keyword rules out a whole class of bugs.

11.2 Running It

public class Main {
    public static void main(String[] args) {
        BankAccount a = new BankAccount("Suraj");
        BankAccount b = new BankAccount("Shweta", 5000);
        BankAccount c = new BankAccount(b);      // copy of Shweta's account

        System.out.println(a);   // Output: 1 | Suraj | 0.0
        System.out.println(b);   // Output: 2 | Shweta | 5000.0
        System.out.println(c);   // Output: 3 | Shweta | 5000.0

        new BankAccount("Ravi", -100);  // throws IllegalArgumentException
    }
}

Read the account numbers. Each object gets the next value because the static counter belongs to the class, not to any single account.

The final line never produces an object. Validation throws first, so a negative balance simply cannot exist in this system. That guarantee is exactly what a good constructor buys you.

12. Interview Questions

Q: What is a constructor in Java?

A: A constructor is a special block of code that carries the same name as its class and declares no return type. Java runs it automatically during new, and its job is to initialise the object’s instance variables before anyone uses it.

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

A: A no-argument constructor is one you write yourself with an empty parameter list, and it can contain any code. The default constructor is the one the compiler generates when your class declares no constructor at all. That generated version only calls super(), so every field keeps its zero value, and it disappears the moment you write any constructor of your own.

Q: Can a constructor be private in Java?

A: Yes. A private constructor stops any outside code from calling new, which is how the singleton pattern limits a class to one object. Utility classes such as java.lang.Math use the same trick to block instantiation entirely. Enum constructors are private automatically.

Q: Why can a constructor not be static, final, or abstract?

A: A constructor exists to build one object, so static contradicts its whole purpose. No subclass can ever override a constructor, which makes final pointless. An abstract member carries no body, yet a constructor must always run statements. The compiler rejects all three.

Q: Can we override a constructor in Java?

A: No. A subclass never inherits its parent’s constructors, and overriding requires inheritance. You can overload constructors within one class, and a subclass can call a parent constructor through super(), but neither of those is overriding.

Q: What is constructor chaining in Java?

A: Constructor chaining means one constructor calls another. Use this() for another constructor in the same class and super() for the parent class. Either call must come first, and only one of them may appear. Chaining keeps the real initialisation logic in a single constructor.

Q: What is the order of execution of static block, instance block, and constructor?

A: Static blocks run once when the JVM loads the class, parent before child. Then, for each new object, the constructor starts with super(), instance field initialisers and instance blocks run in source order, and the constructor body runs last. So the sequence is parent static, child static, parent instance block, parent constructor, child instance block, child constructor.

Q: What happens if the parent class has no no-argument constructor?

A: The compiler inserts a bare super() at the top of every child constructor that lacks an explicit this() or super(). When the parent declares only a parameterized constructor, that inserted call matches nothing and compilation fails. Fix it by calling super(args) explicitly or by adding a no-argument constructor to the parent.

Q: Does Java have a copy constructor?

A: Not as a built-in feature like C++. You write one yourself as a constructor that takes an object of the same class and copies its fields. Watch out for mutable fields such as lists, because copying the reference gives a shallow copy where both objects share one list. Build a new collection inside the constructor for a deep copy.

Q: Can a constructor throw an exception?

A: Yes, and this is a common way to guard your data. A constructor may declare a throws clause or throw an unchecked exception such as IllegalArgumentException after validating its arguments. When it throws, no usable object reaches the caller, so an invalid object never enters your program.

13. Conclusion

Let us wrap up what we covered. A constructor shares the class name, declares no return type, and runs automatically during new.

You met three shapes: the no-argument form, the parameterized form, and the hand-written copy constructor. Remember that the compiler’s default constructor disappears as soon as you declare one of your own, and that copying a mutable field needs a deep copy.

Overloading gives callers several ways in, while this() and super() funnel them into one place. Both calls must come first, and Java inserts a bare super() when you write neither.

Keep the initialisation order in mind: static blocks once per class, then the parent, then the child. Finally, use private constructors for singletons and utility classes, and validate your arguments so a broken object never gets built.

Further Reading

Leave a Comment