Constructors
A constructor is a special method that runs automatically when you create an object with new. Its job is to initialize the object’s fields so every new object starts in a valid, predictable state — not full of garbage values.
What Makes a Constructor Special
A constructor looks like a method but follows three strict rules:
- Its name must exactly match the class name (including case).
- It has no return type — not even
void. - It is called automatically by the JVM the moment
newallocates memory for the object.
public class Car {
String brand;
int year;
// constructor — same name as class, no return type
Car(String brand, int year) {
this.brand = brand;
this.year = year;
}
}
Note: The
thiskeyword used above refers to the current object. It lets you disambiguate between a field (this.brand) and a constructor parameter that has the same name (brand).
Default Constructor
If you do not write any constructor, the Java compiler silently inserts a default (no-argument) constructor that does nothing except call super(). The moment you write even one constructor yourself, the compiler stops generating the default one.
public class Point {
int x;
int y;
// No constructor written — compiler adds:
// Point() { super(); }
}
public class Main {
public static void main(String[] args) {
Point p = new Point(); // works fine
System.out.println(p.x + ", " + p.y); // 0, 0 (int fields default to 0)
}
}
Output:
0, 0
Warning: Once you add a parameterized constructor,
new Point()will fail to compile unless you also explicitly write a no-arg constructor. This is a very common beginner mistake.
Parameterized Constructor
A parameterized constructor accepts arguments so you can supply initial values at the moment of creation — no need for separate setter calls.
public class Rectangle {
double width;
double height;
Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
double area() {
return width * height;
}
}
public class Main {
public static void main(String[] args) {
Rectangle r = new Rectangle(5.0, 3.0);
System.out.println("Area: " + r.area());
}
}
Output:
Area: 15.0
Constructor Overloading
Just like method overloading, you can define multiple constructors in the same class as long as their parameter lists differ. The compiler picks the right one based on the arguments you pass.
public class Student {
String name;
int age;
// no-arg constructor — sensible defaults
Student() {
this.name = "Unknown";
this.age = 0;
}
// one-arg constructor
Student(String name) {
this.name = name;
this.age = 0;
}
// full constructor
Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + " (age " + age + ")";
}
}
public class Main {
public static void main(String[] args) {
Student s1 = new Student();
Student s2 = new Student("Alice");
Student s3 = new Student("Bob", 22);
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
}
}
Output:
Unknown (age 0)
Alice (age 0)
Bob (age 22)
Constructor Chaining with this()
When one constructor calls another constructor in the same class, that is called constructor chaining. You use the special this(...) call for this. It must be the very first statement in the constructor body.
public class Circle {
double radius;
String color;
Circle() {
this(1.0, "black"); // delegates to the full constructor
}
Circle(double radius) {
this(radius, "black");
}
Circle(double radius, String color) {
this.radius = radius;
this.color = color;
}
@Override
public String toString() {
return color + " circle, r=" + radius;
}
}
public class Main {
public static void main(String[] args) {
System.out.println(new Circle());
System.out.println(new Circle(5.0));
System.out.println(new Circle(3.0, "red"));
}
}
Output:
black circle, r=1.0
black circle, r=5.0
red circle, r=3.0
Constructor chaining keeps initialization logic in one place instead of duplicating it across multiple constructors — a clean, DRY approach.
Tip:
this()andsuper()cannot both be the first statement. Java allows only one explicit constructor call at the top of a constructor body.
Calling the Parent Constructor with super()
When a class inherits from another, the child constructor must initialize the parent’s fields too. You do this with super(...). If you omit it, the compiler silently inserts super() (the no-arg parent constructor). If the parent has no no-arg constructor, the code won’t compile.
public class Animal {
String name;
Animal(String name) {
this.name = name;
}
}
public class Dog extends Animal {
String breed;
Dog(String name, String breed) {
super(name); // initialize Animal's field
this.breed = breed;
}
@Override
public String toString() {
return name + " (" + breed + ")";
}
}
public class Main {
public static void main(String[] args) {
Dog d = new Dog("Rex", "Labrador");
System.out.println(d);
}
}
Output:
Rex (Labrador)
See super Keyword for a full breakdown of super usage beyond constructors.
Copy Constructor
Java does not have a built-in copy constructor like C++, but the pattern is simple to implement manually: write a constructor that takes an object of the same type and copies its fields.
public class Point {
int x;
int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
// copy constructor
Point(Point other) {
this.x = other.x;
this.y = other.y;
}
}
public class Main {
public static void main(String[] args) {
Point original = new Point(3, 7);
Point copy = new Point(original);
copy.x = 99; // modifying copy does NOT affect original
System.out.println("original: " + original.x + ", " + original.y);
System.out.println("copy : " + copy.x + ", " + copy.y);
}
}
Output:
original: 3, 7
copy : 99, 7
Warning: For fields that are reference types (arrays, other objects), a copy constructor doing
this.field = other.fieldperforms only a shallow copy — both objects point at the same inner object. For a deep copy you must recursively copy those references. See Object Cloning for the alternativeclone()approach.
Private Constructors
Marking a constructor private prevents anyone outside the class from calling new. This is the backbone of the Singleton and Utility class patterns.
public class MathUtils {
// Utility class — no instances allowed
private MathUtils() { }
public static int square(int n) {
return n * n;
}
}
// MathUtils m = new MathUtils(); // compile error!
System.out.println(MathUtils.square(5)); // 25
See static Keyword for more on why utility classes pair naturally with static methods.
Under the Hood
When you write new Car("Toyota", 2023), the JVM takes these steps:
- Memory allocation — The heap allocator reserves memory for a new
Carobject. All fields are zeroed out (numerics →0, booleans →false, references →null). - Constructor dispatch — The JVM pushes arguments onto the operand stack and invokes the matching constructor via the
invokespecialbytecode instruction.invokespecialis used (rather thaninvokevirtual) because constructors are not polymorphic — they are always resolved at compile time. - Parent initialization — Before anything else runs in your constructor, the JVM ensures
super()orsuper(...)has been called, walking all the way up the chain toObject. - Instance initializer blocks run after
super()but before the rest of your constructor body (see Instance Initializer Block). - Return of reference — The
newexpression evaluates to a reference to the fully initialized object, which is stored in your variable.
This sequencing guarantees that an object is never observable in a partially constructed state through normal code paths. The JVM spec calls this the creation and initialization protocol.
Note: Constructors are not inherited. A subclass must always define its own constructors (even if it just forwards to
super). This is whyObject— the root of all classes — provides its own no-arg constructor, so every class can implicitly chain up to it.
Quick Reference
| Feature | Syntax | Notes |
|---|---|---|
| No-arg constructor | ClassName() { } | Compiler auto-generates if none exists |
| Parameterized | ClassName(Type p) { } | Any number of parameters |
| Overloaded | Multiple constructors, different params | Resolved at compile time |
| Chain to same class | this(args) — first line | Avoids duplicating init logic |
| Chain to parent | super(args) — first line | Required if parent has no no-arg |
| Copy constructor | ClassName(ClassName other) | Must deep-copy reference fields manually |
| Private constructor | private ClassName() { } | Singleton, factory, utility classes |
Related Topics
- Classes & Objects — understand the blueprint before exploring how it gets initialized
- this Keyword — how
thisdisambiguates fields from parameters and chains constructors - super Keyword — calling parent constructors and methods in an inheritance hierarchy
- Instance Initializer Block — another initialization mechanism that runs just before the constructor body
- Object Cloning — an alternative to copy constructors for duplicating objects
- Inheritance — why constructor chaining with
super()matters so much in subclasses