Constructors in Java
-
Last Updated: April 20, 2025
-
By: javahandson
-
Series
Learn constructors in Java the easy way: default, parameterized and copy constructors, overloading, chaining with this() and super(), initialization order, and private constructors.
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.
We begin with the plain idea, then work up to the parts that trip people in interviews. Here is the plan:
this() and super()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.
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.
Beginners often ask why a constructor cannot just be a normal method. The differences are small on screen but large in behaviour.
| Point | Constructor | Method |
|---|---|---|
| Name | Same as the class | Any valid name |
| Return type | None at all | Required, even void |
| Who calls it | Java, during new | You, by name |
| How often | Once per object | As often as you like |
| Inheritance | A subclass never inherits it | A subclass inherits it |
| Overriding | Impossible | Allowed |
| Overloading | Allowed | Allowed |
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.
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.
Java developers usually name three kinds. Two come from the language, and one you write by hand.
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.
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 type | Default value |
|---|---|
byte, short, int, long | 0 |
float, double | 0.0 |
char | the null character (Unicode code point zero) |
boolean | false |
| String or any object | null |
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.
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.
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.
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.
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.
That third bullet catches people out. Two constructors taking a single String will never compile, however different the parameter names look.
Constructor chaining means one constructor calls another. Java offers two forms, and each targets a different class.
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 finishedRead that output carefully. The target constructor finishes first, then control returns to the caller. Chaining runs inside out, much like nested boxes.
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 101The 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.
Both calls follow one strict rule. A this() or super() call must sit as the first statement of the constructor.
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.
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.
Interviewers adore this question. Several things run when you create an object, and the sequence never varies.
new, the JVM allocates memory and sets every field to its zero value.this() or super(), explicit or inserted.super() returns, instance field initialisers and instance blocks run in source order.Three words summarise it: static first, then parent, then child.
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 constructorNotice 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.
These two look similar in a file, yet they serve different masters. The table sorts them out.
| Point | Static Block | Constructor |
|---|---|---|
| Belongs to | The class | Each object |
| Initialises | Static variables | Instance variables |
| Runs | Once, at class loading | Once per new |
| Syntax | static { } | Class name, no return type |
| Parameters | Never | Any number |
this and super | Unavailable | Available |
| Overloading | Not a thing | Fully supported |
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.
Two rules define a constructor, and both are absolute.
void.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.
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.
static constructor makes no sense, because the whole job is building one object.final keyword blocks overriding, and nobody can override a constructor.abstract constructor would carry no body, yet a constructor must always run something.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.
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.
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.
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; }
}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.
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.
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.
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 constructorThis 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”.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.