Constructors in Java
Learn constructors in Java in detail, including default constructors, no-argument constructors, parameterized constructors, constructor overloading, constructor chaining, this(), super(), initialization order, private constructors, constructor access modifiers, object validation, and real-world constructor design.
Introduction
In the previous lesson, you learned how objects are created from classes. You also saw expressions such as new Student(). The special class member that runs during object creation is called a constructor.
class Student {
String name;
int age;
Student(
String name,
int age
) {
this.name = name;
this.age = age;
}
}
public class Main {
public static void main(
String[] args
) {
Student student =
new Student(
"Aarav",
21
);
System.out.println(
student.name
);
System.out.println(
student.age
);
}
}The Student constructor receives the values required to initialize the new object. Every Student object created through this constructor starts with a name and age.
- What a constructor is.
- Why constructors are needed.
- How constructor syntax works.
- The main characteristics of constructors.
- The difference between constructors and methods.
- When constructors execute.
- How constructors participate in object creation.
- What a default constructor is.
- When the compiler provides a constructor.
- What a no-argument constructor is.
- The difference between default and no-argument constructors.
- How parameterized constructors work.
- How constructors initialize fields.
- How the this keyword works.
- What parameter shadowing is.
- How constructor overloading works.
- Why classes provide multiple constructors.
- Constructor overloading rules.
- What constructor signatures contain.
- How ambiguous constructor calls occur.
- What constructor chaining is.
- How this() calls another constructor.
- The rules of this().
- How constructor chaining removes duplicate code.
- How constructor execution flows through a chain.
- Why recursive constructor invocation is illegal.
- How super() calls a parent constructor.
- When Java inserts implicit super().
- How to call parameterized parent constructors.
- The difference between this() and super().
- How constructors execute in inheritance.
- Why constructors are not inherited.
- How constructor access modifiers work.
- How public constructors work.
- How protected constructors work.
- How package-private constructors work.
- How private constructors work.
- Common uses of private constructors.
- How classes provide multiple construction paths.
- How constructors validate object state.
- How constructors prevent invalid objects.
- How constructors throw exceptions.
- Why defensive copying matters.
- How mutable constructor arguments can create problems.
- How constructors initialize final fields.
- How constructors interact with static fields.
- How constructors initialize instance fields.
- The order of field initialization.
- How instance initialization blocks work.
- The complete object initialization order.
- How static initialization differs from object initialization.
- How constructors receive object references.
- How constructors create nested objects.
- What constructor injection is.
- The basic idea of dependency injection.
- What a copy constructor pattern is.
- The difference between shallow and deep copy constructors.
- How constructors receive arrays.
- How varargs constructors work.
- How constructors receive collections.
- How constructors work with generic classes.
- How abstract class constructors work.
- Why interfaces do not have constructors.
- How enum constructors work.
- How record constructors work.
- What canonical and compact record constructors are.
- What constructor references are.
- When factory methods are useful.
- When builders are useful.
- What the telescoping constructor problem is.
- How to design constructors for real-world objects.
- Common constructor errors.
- Constructor best practices.
What is a Constructor?
A constructor is a special class member that is invoked during object creation. Its primary purpose is to initialize a newly created object into a valid starting state.
class Student {
String name;
Student() {
name = "Unknown";
}
}new Student()
│
▼
OBJECT MEMORY CREATED
│
▼
FIELDS INITIALIZED
│
▼
CONSTRUCTOR EXECUTES
│
▼
OBJECT READY FOR USE- A constructor belongs to a class.
- A constructor initializes a new object.
- A constructor has the same name as the class.
- A constructor has no return type.
- A constructor runs during object creation.
- A constructor can receive parameters.
- A constructor can be overloaded.
- A constructor can call another constructor.
Why Constructors are Needed
Valid Initialization
Constructors ensure objects begin with meaningful and valid state.
Required Data
Required values can be demanded when an object is created.
Validation
Invalid data can be rejected before an object becomes available for use.
Reusable Setup
Common object initialization logic can be centralized.
Dependencies
Objects required by another object can be supplied during construction.
Immutability
Final fields can receive their required values during construction.
Real-World Analogy
Think of a constructor as the setup process used when a new employee joins a company.
NEW EMPLOYEE CREATED
│
▼
RECEIVE NAME
│
▼
RECEIVE EMPLOYEE ID
│
▼
ASSIGN DEPARTMENT
│
▼
SET STARTING STATUS
│
▼
EMPLOYEE READY
CONSTRUCTOR
PERFORMS THE INITIAL SETUPConstructor Syntax
class ClassName {
ClassName() {
// Initialization code
}
}class Student {
Student() {
System.out.println(
"Student created"
);
}
}Student() {
}
│
├── Student
│
│ Same name as class
│
├── ()
│
│ Parameter list
│
└── { }
Constructor bodyConstructor Characteristics
- A constructor has the same name as its class.
- A constructor has no return type.
- Not even void is written as a constructor return type.
- A constructor runs during object creation.
- A constructor can have parameters.
- A constructor can be overloaded.
- A constructor can use access modifiers.
- A constructor can call another constructor using this().
- A constructor can call a parent constructor using super().
- Constructors are not inherited.
- Constructors cannot be overridden.
- A constructor cannot be abstract.
- A constructor cannot be static.
- A constructor cannot be final.
- A constructor cannot be synchronized.
- A constructor can throw exceptions.
Constructor vs Method
CONSTRUCTOR
Same name as class
No return type
Runs during object creation
Initializes object state
Cannot be inherited
Cannot be overridden
Can call this() or super()
METHOD
Can have any valid method name
Has return type or void
Runs when explicitly called
Performs object or class behavior
Can be inherited
Can be overridden
Cannot use this() or super()
as constructor callsclass Student {
Student() {
System.out.println(
"Constructor"
);
}
void study() {
System.out.println(
"Method"
);
}
}When a Constructor Runs
class Student {
Student() {
System.out.println(
"Constructor executed"
);
}
}
public class Main {
public static void main(
String[] args
) {
Student first =
new Student();
Student second =
new Student();
}
}Constructor executed
Constructor executedThe constructor executes once for each new object created through that constructor call.
Object Creation Process
Student student =
new Student(
"Aarav",
21
);STEP 1
new requests object creation
STEP 2
Memory is allocated
STEP 3
Instance fields receive default values
STEP 4
Parent initialization begins
STEP 5
Explicit field initializers run
STEP 6
Instance initialization blocks run
STEP 7
Constructor body executes
STEP 8
Reference is returned
STEP 9
Reference is assigned to studentDefault Constructor
A default constructor is a no-argument constructor automatically provided by the Java compiler only when the class declares no constructor.
class Student {
String name;
int age;
}Student student =
new Student();The compiler provides a default constructor because the Student class does not declare any constructor.
Compiler-Provided Constructor
SOURCE CLASS
class Student {
}
CONCEPTUALLY
COMPILER PROVIDES
Student() {
super();
}- The compiler provides a default constructor only when no constructor is declared.
- If you declare any constructor, the compiler does not add the default constructor.
- The default constructor has no parameters.
- Its accessibility corresponds to the accessibility of the class.
No-Argument Constructor
A no-argument constructor is any constructor that accepts zero arguments. It may be written explicitly by the programmer.
class Student {
String name;
int age;
Student() {
name = "Unknown";
age = 18;
}
}Default Constructor vs No-Argument Constructor
DEFAULT CONSTRUCTOR
Provided by compiler
Only when class declares no constructor
Has zero parameters
NO-ARGUMENT CONSTRUCTOR
Has zero parameters
Can be written by programmer
Can contain custom initialization logic
IMPORTANT
Every default constructor
is a no-argument constructor
But every no-argument constructor
is not a default constructorParameterized Constructor
A parameterized constructor accepts values required to initialize a new object.
class Student {
String name;
int age;
Student(
String studentName,
int studentAge
) {
name = studentName;
age = studentAge;
}
}Student student =
new Student(
"Aarav",
21
);Initializing Fields with Constructors
class Product {
String name;
double price;
int quantity;
Product(
String productName,
double productPrice,
int productQuantity
) {
name = productName;
price = productPrice;
quantity = productQuantity;
}
}The constructor requires the values necessary to create a complete Product object.
The this Keyword in Constructors
The this keyword refers to the current object whose constructor or instance method is executing.
class Student {
String name;
int age;
Student(
String name,
int age
) {
this.name = name;
this.age = age;
}
}this.name = name;
│ │
│ └── Constructor Parameter
│
└────────── Current Object FieldParameter Shadowing
class Student {
String name;
Student(String name) {
name = name;
}
}Both names in name = name refer to the constructor parameter. The instance field remains unchanged.
Student(String name) {
this.name = name;
}Constructor Overloading
Constructor overloading means declaring multiple constructors in the same class with different parameter lists.
class Student {
String name;
int age;
Student() {
name = "Unknown";
age = 18;
}
Student(String name) {
this.name = name;
age = 18;
}
Student(
String name,
int age
) {
this.name = name;
this.age = age;
}
}Student first =
new Student();
Student second =
new Student("Aarav");
Student third =
new Student(
"Meera",
22
);Why Overload Constructors?
Flexible Creation
Objects can be created using different available data.
Default Values
Missing optional values can receive sensible defaults.
Different Use Cases
Different application scenarios can use different construction paths.
Readability
Object creation can remain clear without long setup code after construction.
Constructor Overloading Rules
- Constructors must have different parameter lists.
- The number of parameters can differ.
- Parameter types can differ.
- Parameter order can differ when the types make the signatures different.
- Parameter names alone cannot overload constructors.
- Access modifiers do not create different signatures.
- The compiler selects the matching constructor from the arguments.
Constructor Signatures
Student()
Student(String name)
Student(int age)
Student(
String name,
int age
)
Student(
int age,
String name
)A constructor signature is determined by its parameter types and their order, not by parameter names.
Ambiguous Constructor Calls
class Example {
Example(String value) {
}
Example(Integer value) {
}
}new Example(null);The call is ambiguous because null is compatible with both unrelated reference types and neither constructor is more specific than the other.
Constructor Chaining
Constructor chaining means one constructor invokes another constructor so that initialization logic can be reused.
CONSTRUCTOR A
│
│ this(...)
▼
CONSTRUCTOR B
│
│ this(...)
▼
CONSTRUCTOR C
│
▼
COMMON INITIALIZATIONThe this() Constructor Call
class Student {
String name;
int age;
Student() {
this(
"Unknown",
18
);
}
Student(String name) {
this(
name,
18
);
}
Student(
String name,
int age
) {
this.name = name;
this.age = age;
}
}Rules of this()
- this() calls another constructor in the same class.
- this() must be the first statement in the constructor.
- Only one explicit constructor invocation can appear first.
- A constructor cannot directly or indirectly call itself forever.
- this() and super() cannot both appear as explicit constructor invocations in the same constructor.
Avoiding Duplicate Constructor Code
Student() {
name = "Unknown";
age = 18;
active = true;
}
Student(String name) {
this.name = name;
age = 18;
active = true;
}Student() {
this(
"Unknown",
18,
true
);
}
Student(String name) {
this(
name,
18,
true
);
}
Student(
String name,
int age,
boolean active
) {
this.name = name;
this.age = age;
this.active = active;
}Constructor Chaining Flow
Student() {
this("Unknown");
System.out.println("A");
}
Student(String name) {
this(name, 18);
System.out.println("B");
}
Student(
String name,
int age
) {
this.name = name;
this.age = age;
System.out.println("C");
}Student()
│
▼
Student(String)
│
▼
Student(String, int)
│
▼
PRINT C
│
▼
RETURN TO Student(String)
PRINT B
│
▼
RETURN TO Student()
PRINT ARecursive Constructor Invocation
class Example {
Example() {
this(10);
}
Example(int value) {
this();
}
}This creates a constructor invocation cycle and causes a compile-time error.
The super() Constructor Call
The super() constructor invocation calls a constructor of the immediate parent class.
class Person {
Person() {
System.out.println(
"Person constructor"
);
}
}
class Student extends Person {
Student() {
super();
System.out.println(
"Student constructor"
);
}
}Implicit super()
If a constructor does not explicitly begin with this() or super(), Java inserts an implicit no-argument super() call.
Student() {
System.out.println(
"Student created"
);
}Student() {
super();
System.out.println(
"Student created"
);
}Calling a Parameterized Parent Constructor
class Person {
String name;
Person(String name) {
this.name = name;
}
}
class Student extends Person {
int rollNumber;
Student(
String name,
int rollNumber
) {
super(name);
this.rollNumber =
rollNumber;
}
}this() vs super()
this()
Calls constructor
in same class
Used for constructor chaining
Must be first statement
super()
Calls constructor
in immediate parent class
Used for parent initialization
Must be first statement
ONE CONSTRUCTOR
CANNOT EXPLICITLY START WITH BOTHConstructor Execution in Inheritance
class Person {
Person() {
System.out.println("Person");
}
}
class Student extends Person {
Student() {
System.out.println("Student");
}
}
new Student();Person
StudentParent construction completes before the child constructor body executes.
Constructors are Not Inherited
A child class does not inherit the constructors of its parent class. The child declares its own constructors and may invoke parent constructors using super().
- Constructors initialize instances of their own class.
- A parent constructor creates the parent portion of a child object.
- A child class may require different initialization data.
- Constructor names must match their own class names.
Constructor Access Modifiers
public
protected
package-private
privateConstructor accessibility controls where objects can be created directly.
public Constructors
public Student(
String name
) {
this.name = name;
}A public constructor can be accessed from any location where the class itself is accessible.
protected Constructors
protected Student() {
}A protected constructor is commonly useful when construction should be available within the package and through subclass-related access rules.
Package-Private Constructors
Student() {
}A constructor without an explicit access modifier is accessible within the same package.
private Constructors
class Utility {
private Utility() {
}
}A private constructor prevents direct object creation from outside the class.
Private Constructor Use Cases
Utility Classes
Prevent objects when a class only provides static operations.
Factory Methods
Force object creation through controlled static methods.
1️⃣ Singleton Pattern
Control the number of class instances.
Controlled Creation
Validate or cache objects before returning them.
Multiple Constructors
class Product {
String name;
double price;
int quantity;
Product() {
this(
"Unknown",
0.0,
0
);
}
Product(String name) {
this(
name,
0.0,
0
);
}
Product(
String name,
double price
) {
this(
name,
price,
0
);
}
Product(
String name,
double price,
int quantity
) {
this.name = name;
this.price = price;
this.quantity = quantity;
}
}Constructor Validation
Constructors can validate required values before storing them in the new object.
class Product {
String name;
double price;
Product(
String name,
double price
) {
if (
name == null
|| name.isBlank()
) {
throw new IllegalArgumentException(
"Name is required"
);
}
if (price < 0) {
throw new IllegalArgumentException(
"Price cannot be negative"
);
}
this.name = name;
this.price = price;
}
}Preventing Invalid Objects
OBJECT CREATED
name = null
price = -5000
quantity = -10
INVALID OBJECT EXISTSINPUT RECEIVED
│
▼
VALIDATE
│
├── INVALID
│ │
│ ▼
│ REJECT CREATION
│
└── VALID
│
▼
INITIALIZE OBJECT
│
▼
VALID OBJECT READYThrowing Exceptions from Constructors
class BankAccount {
double balance;
BankAccount(
double initialBalance
) {
if (initialBalance < 0) {
throw new IllegalArgumentException(
"Initial balance cannot be negative"
);
}
balance = initialBalance;
}
}If construction fails by throwing an exception, the caller does not receive a successfully constructed object reference from that expression.
Defensive Copying in Constructors
A constructor can create a defensive copy of mutable input instead of storing the caller-owned mutable object directly.
class Student {
private int[] marks;
Student(int[] marks) {
this.marks =
marks.clone();
}
}Mutable Constructor Arguments
class Student {
int[] marks;
Student(int[] marks) {
this.marks = marks;
}
}
int[] marks = {
80,
90
};
Student student =
new Student(marks);
marks[0] = 0;The external array and the object field refer to the same mutable array. External changes can therefore affect the object.
Constructors and final Fields
class Student {
final int rollNumber;
final String name;
Student(
int rollNumber,
String name
) {
this.rollNumber =
rollNumber;
this.name = name;
}
}A blank final instance field must be assigned exactly once during object initialization, commonly inside every constructor path.
Constructors and static Fields
class Student {
static int count;
Student() {
count++;
}
}
new Student();
new Student();
new Student();
System.out.println(
Student.count
);3Constructors and Instance Fields
class Student {
String name;
Student(String name) {
this.name = name;
}
}
Student first =
new Student("Aarav");
Student second =
new Student("Meera");Each constructor invocation initializes the fields of a different object.
Field Initialization Order
class Example {
int first = 10;
int second =
first + 5;
Example() {
System.out.println(
second
);
}
}Instance field initializers execute in textual order as part of object initialization before the constructor body of that class.
Instance Initialization Blocks
class Student {
String name = "Unknown";
{
System.out.println(
"Instance block"
);
}
Student() {
System.out.println(
"Constructor"
);
}
}FIELD INITIALIZERS
│
▼
INSTANCE INITIALIZATION BLOCKS
│
▼
CONSTRUCTOR BODYObject Initialization Order
1. MEMORY ALLOCATED
2. FIELDS RECEIVE DEFAULT VALUES
3. PARENT INITIALIZATION COMPLETES
4. INSTANCE FIELD INITIALIZERS RUN
5. INSTANCE INITIALIZATION BLOCKS RUN
6. CONSTRUCTOR BODY RUNS
7. OBJECT CREATION COMPLETESStatic Initialization Order
FIRST ACTIVE CLASS USE
│
▼
STATIC FIELD INITIALIZERS
│
▼
STATIC INITIALIZATION BLOCKS
│
▼
CLASS INITIALIZED
EACH NEW OBJECT
│
▼
INSTANCE FIELD INITIALIZERS
│
▼
INSTANCE INITIALIZATION BLOCKS
│
▼
CONSTRUCTOR BODYComplete Initialization Sequence
CLASS INITIALIZATION
1. Parent static initialization
2. Child static initialization
OBJECT CREATION
3. Object memory allocated
4. Fields receive default values
5. Parent instance field initializers
6. Parent instance blocks
7. Parent constructor body
8. Child instance field initializers
9. Child instance blocks
10. Child constructor body
11. Object readyConstructors and Object References
class Student {
Address address;
Student(Address address) {
this.address = address;
}
}The constructor receives a copied reference value. Unless a defensive copy is created, the Student and caller may refer to the same Address object.
Passing Objects to Constructors
Address address =
new Address(
"Mumbai",
"India"
);
Student student =
new Student(
"Aarav",
address
);STUDENT OBJECT
address reference
│
▼
ADDRESS OBJECTCreating Nested Objects in Constructors
class Car {
private Engine engine;
Car() {
engine =
new Engine();
}
}The Car constructor creates and owns its Engine object, representing a stronger lifecycle relationship.
Constructor Injection
Constructor injection means supplying an object with its required dependencies through constructor parameters.
class EmailService {
void send() {
System.out.println(
"Email sent"
);
}
}
class NotificationManager {
private final EmailService emailService;
NotificationManager(
EmailService emailService
) {
this.emailService =
emailService;
}
}Dependency Injection Concept
NOTIFICATION MANAGER
Creates its own dependency
Strongly controls dependency creationEXTERNAL CODE
Creates EmailService
│
▼
PASSES DEPENDENCY
│
▼
NotificationManager
Receives required dependency- Required dependencies are explicit.
- Objects can be fully initialized.
- Dependencies can be replaced for testing.
- Fields can often be final.
- The pattern is widely used by frameworks such as Spring.
Copy Constructor Pattern
Java has no special built-in copy constructor feature, but a class can declare a constructor that accepts another object of the same type.
class Student {
String name;
int age;
Student(
String name,
int age
) {
this.name = name;
this.age = age;
}
Student(Student other) {
this.name = other.name;
this.age = other.age;
}
}Shallow Copy Constructor
Student(Student other) {
this.name = other.name;
this.address =
other.address;
}The new Student object receives the same Address reference, so both Student objects share one Address object.
Deep Copy Constructor
Student(Student other) {
this.name = other.name;
this.address =
new Address(
other.address
);
}The new Student receives a separate Address object, assuming the Address copy constructor correctly copies its state.
Constructors with Arrays
class Student {
private final int[] marks;
Student(int[] marks) {
this.marks =
marks.clone();
}
}Constructors with Varargs
class Team {
String[] members;
Team(String... members) {
this.members =
members.clone();
}
}
Team team =
new Team(
"Aarav",
"Meera",
"Kabir"
);Constructors with Collections
class Course {
private final List<String> topics;
Course(
List<String> topics
) {
this.topics =
new ArrayList<>(
topics
);
}
}Copying the collection prevents the caller from changing the internal list structure through the original list reference.
Constructors in Generic Classes
class Box<T> {
private T value;
Box(T value) {
this.value = value;
}
}
Box<String> textBox =
new Box<>("Java");
Box<Integer> numberBox =
new Box<>(100);Constructors in Abstract Classes
An abstract class can have constructors even though the abstract class cannot be instantiated directly.
abstract class Person {
String name;
Person(String name) {
this.name = name;
}
}
class Student extends Person {
Student(String name) {
super(name);
}
}Constructors in Interfaces
Interfaces do not have constructors because interfaces are not instantiated and do not contain normal instance initialization state.
- Interfaces cannot be instantiated.
- Interfaces do not declare constructors.
- Implementing classes declare their own constructors.
- Constructors are not inherited from interfaces.
Constructors in Enums
enum Status {
ACTIVE("Active User"),
INACTIVE("Inactive User");
private final String label;
Status(String label) {
this.label = label;
}
}Enum constructors initialize enum constants. They cannot be public or protected because enum instances are controlled by the enum declaration.
Constructors in Records
record Student(
String name,
int age
) {
}Records automatically receive a canonical constructor corresponding to their record components.
Canonical Record Constructor
record Student(
String name,
int age
) {
public Student(
String name,
int age
) {
this.name = name;
this.age = age;
}
}Compact Record Constructor
record Student(
String name,
int age
) {
public Student {
if (age < 0) {
throw new IllegalArgumentException(
"Age cannot be negative"
);
}
}
}A compact record constructor is commonly used for validation without repeating the full parameter list and field assignments.
Constructor References
Supplier<Student> creator =
Student::new;
Student student =
creator.get();A constructor reference uses the ClassName::new syntax and can be used where a compatible functional interface is expected.
Factory Method vs Constructor
class User {
private User() {
}
static User create() {
return new User();
}
}CONSTRUCTOR
new User(...)
Fixed class name
Creates new object directly
FACTORY METHOD
User.create(...)
Can have meaningful name
Can return cached object
Can return subtype
Can control creation logicBuilder vs Constructor
new User(
"Aarav",
21,
"Mumbai",
true,
"ADMIN"
)User.builder()
.name("Aarav")
.age(21)
.city("Mumbai")
.active(true)
.role("ADMIN")
.build()Constructors are excellent for a small number of required values. Builders can improve readability when many optional parameters exist.
Telescoping Constructor Problem
User(String name)
User(
String name,
int age
)
User(
String name,
int age,
String city
)
User(
String name,
int age,
String city,
boolean active
)
User(
String name,
int age,
String city,
boolean active,
String role
)A large chain of overloaded constructors can become difficult to read and maintain. A builder or factory method may be clearer when many values are optional.
Real-World Student Example
class Student {
private final int rollNumber;
private String name;
private int age;
Student(
int rollNumber,
String name
) {
this(
rollNumber,
name,
18
);
}
Student(
int rollNumber,
String name,
int age
) {
if (rollNumber <= 0) {
throw new IllegalArgumentException(
"Invalid roll number"
);
}
if (
name == null
|| name.isBlank()
) {
throw new IllegalArgumentException(
"Name is required"
);
}
if (age < 0) {
throw new IllegalArgumentException(
"Age cannot be negative"
);
}
this.rollNumber =
rollNumber;
this.name = name;
this.age = age;
}
}Bank Account Example
class BankAccount {
private final String accountNumber;
private String holderName;
private double balance;
BankAccount(
String accountNumber,
String holderName
) {
this(
accountNumber,
holderName,
0.0
);
}
BankAccount(
String accountNumber,
String holderName,
double initialBalance
) {
if (initialBalance < 0) {
throw new IllegalArgumentException(
"Initial balance cannot be negative"
);
}
this.accountNumber =
accountNumber;
this.holderName =
holderName;
this.balance =
initialBalance;
}
}Product Example
class Product {
private String name;
private double price;
private int quantity;
Product(
String name,
double price
) {
this(
name,
price,
0
);
}
Product(
String name,
double price,
int quantity
) {
if (price < 0) {
throw new IllegalArgumentException(
"Price cannot be negative"
);
}
if (quantity < 0) {
throw new IllegalArgumentException(
"Quantity cannot be negative"
);
}
this.name = name;
this.price = price;
this.quantity = quantity;
}
}Employee Example
class Employee {
private final int employeeId;
private String name;
private Department department;
Employee(
int employeeId,
String name,
Department department
) {
this.employeeId =
employeeId;
this.name = name;
this.department =
department;
}
}Order Example
class Order {
private final String orderId;
private final Customer customer;
private final List<Product> products;
Order(
String orderId,
Customer customer,
List<Product> products
) {
if (
orderId == null
|| orderId.isBlank()
) {
throw new IllegalArgumentException(
"Order ID is required"
);
}
if (customer == null) {
throw new IllegalArgumentException(
"Customer is required"
);
}
this.orderId = orderId;
this.customer = customer;
this.products =
new ArrayList<>(
products
);
}
}Immutable Object Example
final class User {
private final String username;
private final String email;
User(
String username,
String email
) {
if (
username == null
|| username.isBlank()
) {
throw new IllegalArgumentException(
"Username is required"
);
}
if (
email == null
|| email.isBlank()
) {
throw new IllegalArgumentException(
"Email is required"
);
}
this.username = username;
this.email = email;
}
String getUsername() {
return username;
}
String getEmail() {
return email;
}
}Constructor Design Process
STEP 1
IDENTIFY REQUIRED STATE
What must exist
for the object to be valid?
STEP 2
IDENTIFY OPTIONAL STATE
What can receive defaults?
STEP 3
CHOOSE PRIMARY CONSTRUCTOR
Put complete initialization
in one constructor
STEP 4
ADD CONVENIENCE CONSTRUCTORS
Use this(...)
to delegate
STEP 5
VALIDATE INPUT
Reject invalid state
STEP 6
HANDLE MUTABLE INPUT
Share intentionally
or
Create defensive copies
STEP 7
INITIALIZE FINAL FIELDS
Ensure every path assigns them
STEP 8
DEFINE ACCESSIBILITY
public?
protected?
package-private?
private?
STEP 9
CHECK INHERITANCE
Which parent constructor
must be called?
STEP 10
TEST EVERY CONSTRUCTION PATH
Valid values
Invalid values
Default values
null values
Boundary valuesCommon Constructor Errors
CONSTRUCTOR ERRORS
├── Adding a Return Type
├── Writing void Before Constructor
├── Using Wrong Constructor Name
├── Confusing Constructor with Method
├── Expecting Constructor to Return a Value
├── Forgetting Constructor Runs During Object Creation
├── Expecting Compiler Default Constructor After Declaring Another Constructor
├── Calling Missing No-Argument Constructor
├── Forgetting to Initialize Required Fields
├── Leaving Object in Invalid State
├── Forgetting final Field Assignment
├── Assigning final Field More Than Once
├── Writing name = name
├── Forgetting this for Shadowed Fields
├── Creating Duplicate Constructor Signatures
├── Overloading Only by Parameter Names
├── Creating Ambiguous Constructor Calls
├── Excessive Constructor Overloading
├── Telescoping Constructors
├── Duplicating Initialization Logic
├── Forgetting this() Must Be First
├── Forgetting super() Must Be First
├── Trying to Use this() and super() Together
├── Recursive Constructor Invocation
├── Calling Constructor Like Normal Method
├── Forgetting Parent Constructor Requirements
├── Relying on Implicit super() When Parent Has No No-Arg Constructor
├── Thinking Constructors are Inherited
├── Trying to Override a Constructor
├── Declaring Constructor static
├── Declaring Constructor final
├── Declaring Constructor abstract
├── Making Constructor Too Complex
├── Performing Heavy I/O in Constructor
├── Starting Threads During Construction
├── Publishing this Before Construction Completes
├── Calling Overridable Methods from Constructor
├── Accepting null Without Clear Meaning
├── Missing Input Validation
├── Storing Mutable Input Directly
├── Forgetting Defensive Copies
├── Creating Accidental Shared State
├── Shallow Copying Nested Mutable Objects
├── Incorrect Deep Copy Logic
├── Exposing Partially Initialized Objects
├── Using public Constructor When Creation Should Be Controlled
├── Using private Constructor Without Providing Creation Path
├── Too Many Boolean Constructor Parameters
├── Too Many Same-Type Parameters
├── Unclear Parameter Order
├── Creating Constructor Side Effects
├── Updating Global State Unnecessarily
├── Performing Expensive Database Calls During Construction
├── Catching and Hiding Initialization Errors
├── Allowing Invalid Dependencies
├── Forgetting Constructor Injection for Required Dependencies
├── Allowing Required Dependency to Be null
├── Confusing Factory Method with Constructor
├── Using Builder for Very Simple Objects
├── Using Huge Constructor for Many Optional Values
├── Misunderstanding Initialization Order
├── Accessing Child State from Parent Construction Unsafely
├── Assuming Child Constructor Runs Before Parent Constructor
├── Assuming Static Initialization Runs for Every Object
├── Assuming Instance Blocks Run After Constructor Body
├── Incorrect Copy Constructor Ownership
├── Treating Java Copy Constructor as Built-In Feature
├── Forgetting Enum Constructor Restrictions
├── Trying to Add Constructor to Interface
└── Misunderstanding Record Constructor RulesBest Practices
- Use constructors to create valid objects.
- Require essential data during construction.
- Use meaningful constructor parameters.
- Keep parameter order logical and predictable.
- Avoid too many parameters of the same type.
- Use this.field when parameter names match field names.
- Prefer one primary constructor for complete initialization.
- Delegate convenience constructors using this().
- Avoid duplicate initialization logic.
- Validate required constructor arguments.
- Reject invalid values early.
- Use clear exception messages.
- Do not allow required dependencies to be null.
- Initialize final fields during construction.
- Use final fields for values that should not be reassigned.
- Understand when the compiler provides a default constructor.
- Do not assume a no-argument constructor exists.
- Add an explicit no-argument constructor only when needed.
- Use constructor overloading only when each overload has a clear purpose.
- Avoid ambiguous overloads.
- Avoid excessive telescoping constructors.
- Consider a builder for many optional parameters.
- Consider factory methods when named creation paths improve clarity.
- Use private constructors to control direct object creation.
- Choose constructor accessibility intentionally.
- Understand that constructors are not inherited.
- Use super() to initialize parent state correctly.
- Remember that this() and super() must be first statements.
- Never create recursive constructor chains.
- Understand initialization order.
- Avoid calling overridable methods from constructors.
- Avoid exposing this before construction completes.
- Keep constructors focused on initialization.
- Avoid expensive network or database operations in constructors.
- Avoid surprising external side effects.
- Use defensive copies for mutable input when ownership should remain internal.
- Document intentional sharing of mutable objects.
- Use deep copies when nested mutable state must be independent.
- Use constructor injection for required dependencies.
- Prefer fully initialized objects.
- Avoid setter-based construction for mandatory data.
- Use copy constructors when copying semantics are clear.
- Clearly define whether copying is shallow or deep.
- Clone arrays when internal independence is required.
- Copy mutable collections when protecting internal state.
- Remember that constructors can throw exceptions.
- Test every constructor overload.
- Test invalid constructor arguments.
- Test null values when relevant.
- Test boundary values.
- Test constructor chaining.
- Test inheritance constructor order.
- Test defensive copy behavior.
- Keep constructors readable.
- Refactor complex object creation into factories or builders when appropriate.
Constructor Selection Guide
NO REQUIRED DATA?
│
├── YES ─────► NO-ARG CONSTRUCTOR
│
└── NO
│
▼
REQUIRED DATA EXISTS?
│
└── YES ─────► PARAMETERIZED CONSTRUCTOR
MULTIPLE VALID CREATION PATHS?
│
├── YES ─────► OVERLOADED CONSTRUCTORS
│
└── NO ──────► ONE PRIMARY CONSTRUCTOR
DUPLICATE INITIALIZATION?
│
├── YES ─────► USE this()
│
└── NO ──────► KEEP DIRECT INITIALIZATION
PARENT INITIALIZATION REQUIRED?
│
└── YES ─────► USE super()
MANY OPTIONAL PARAMETERS?
│
├── YES ─────► CONSIDER BUILDER
│
└── NO ──────► CONSTRUCTOR IS FINE
NEED NAMED CREATION PATH?
│
├── YES ─────► CONSIDER FACTORY METHOD
│
└── NO ──────► CONSTRUCTOR IS FINE
MUTABLE INPUT?
│
├── SHARE INTENTIONALLY
│
└── DEFENSIVE COPY
NEED CONTROLLED CREATION?
│
├── YES ─────► PRIVATE CONSTRUCTOR
│ + FACTORY METHOD
│
└── NO ──────► ACCESSIBLE CONSTRUCTORConstructor Checklist
CONSTRUCTOR CHECKLIST
[ ] Constructor name matches class name
[ ] Constructor has no return type
[ ] Required state is identified
[ ] Optional state is identified
[ ] Object becomes valid after construction
[ ] Required parameters are present
[ ] Parameter names are meaningful
[ ] Parameter order is logical
[ ] this.field is used for shadowed fields
[ ] Input values are validated
[ ] Invalid values are rejected
[ ] null behavior is intentional
[ ] final fields are initialized
[ ] Every constructor path initializes required state
[ ] Default constructor behavior is understood
[ ] No-argument constructor exists only when needed
[ ] Parameterized constructors are clear
[ ] Overloaded constructors have different signatures
[ ] Overloads are not ambiguous
[ ] Constructor count remains manageable
[ ] Duplicate initialization is avoided
[ ] this() is used for same-class chaining
[ ] this() is the first statement
[ ] super() is used when parent initialization requires it
[ ] super() is the first statement
[ ] this() and super() are not used together explicitly
[ ] Constructor chain has no cycle
[ ] Parent constructor requirements are satisfied
[ ] Constructor accessibility is intentional
[ ] private constructor has a valid purpose
[ ] Constructors are not mistaken for inherited members
[ ] Constructor logic remains focused
[ ] Expensive external work is avoided
[ ] Overridable methods are not called during construction
[ ] this is not exposed before initialization completes
[ ] Mutable inputs are reviewed
[ ] Defensive copies are created when needed
[ ] Arrays are cloned when independence is required
[ ] Mutable collections are copied when needed
[ ] Copy behavior is clearly shallow or deep
[ ] Required dependencies use constructor injection
[ ] Dependencies are validated
[ ] Initialization order is understood
[ ] Static initialization is not confused with object construction
[ ] Instance blocks are understood
[ ] Parent initialization occurs before child constructor body
[ ] Factory method is considered when naming helps
[ ] Builder is considered for many optional parameters
[ ] Every constructor overload is tested
[ ] Invalid arguments are tested
[ ] Boundary values are tested
[ ] null values are tested when relevant
[ ] Defensive copy behavior is tested
[ ] Constructor chaining is testedCommon Misconceptions
- A constructor is not a normal method.
- A constructor has no return type.
- Writing void creates a method, not a constructor.
- A constructor name must match the class name.
- Constructors run during object creation.
- Constructors do not return object references explicitly.
- The new expression produces the resulting reference.
- The compiler does not always provide a default constructor.
- A default constructor is provided only when no constructor is declared.
- A programmer-written no-argument constructor is not called the compiler-provided default constructor.
- Declaring a parameterized constructor removes automatic default-constructor generation.
- Constructors can be overloaded.
- Constructors cannot be overridden.
- Constructors are not inherited.
- Constructor parameter names do not affect signatures.
- Access modifiers do not create overloaded signatures.
- this() calls another constructor in the same class.
- super() calls a constructor in the parent class.
- this() must be the first statement.
- super() must be the first statement.
- A constructor cannot explicitly begin with both this() and super().
- Constructor invocation cycles are illegal.
- Parent construction happens before the child constructor body.
- If no explicit constructor invocation exists, Java inserts super().
- Implicit super() fails when the parent has no accessible no-argument constructor.
- Private constructors do not mean a class is useless.
- Private constructors can support factories and controlled creation.
- Constructors can throw exceptions.
- A failed constructor does not provide a normally constructed object to the caller.
- Constructor arguments are passed by value.
- Object reference values passed to constructors are copied.
- Storing a mutable reference can create shared state.
- A defensive copy can protect internal state.
- A copy constructor is a design pattern in Java, not special language syntax.
- A shallow copy can share nested mutable objects.
- A deep copy requires copying relevant nested mutable state.
- Final fields can be initialized in constructors.
- Static fields belong to the class, not individual objects.
- Static initialization does not run for every new object.
- Instance field initializers run before the constructor body.
- Instance initialization blocks run before the constructor body.
- Abstract classes can have constructors.
- Interfaces cannot have constructors.
- Enums can have constructors.
- Records have special constructor forms.
- A builder does not replace every constructor.
- Factory methods are not always better than constructors.
- More constructor overloads do not automatically improve design.
- A constructor should initialize an object, not perform arbitrary application workflows.
Practice Exercises
- Create a Student class.
- Add name, age, and marks fields.
- Create a parameterized constructor.
- Initialize all fields.
- Create and display three Student objects.
- Create a Product class.
- Add no-argument, two-argument, and three-argument constructors.
- Use sensible default values.
- Create objects through every constructor.
- Display the resulting state.
- Create three overloaded Employee constructors.
- Use this() to chain them.
- Keep complete initialization in one primary constructor.
- Verify the constructor execution flow.
- Create a BankAccount class.
- Require an account number and initial balance.
- Reject blank account numbers.
- Reject negative balances.
- Test valid and invalid construction.
- Create Person and Student classes.
- Add a parameterized Person constructor.
- Call it using super() from Student.
- Print messages from both constructors.
- Observe execution order.
- Create a Student class containing an int array of marks.
- Receive the array through the constructor.
- Store a defensive copy.
- Modify the original array.
- Verify that Student state remains unchanged.
- Create a Product class.
- Add a constructor that accepts another Product.
- Create an independent copy.
- Modify the copied object.
- Verify that the original remains unchanged.
- Create Repository and Service classes.
- Require Repository in the Service constructor.
- Store the dependency in a final field.
- Create the dependency externally.
- Pass it to the Service object.
- Create an immutable Employee class.
- Use private final fields.
- Initialize all values through the constructor.
- Validate required values.
- Provide only getter methods.
- Create Customer, Product, and Order classes.
- Use constructors for required state.
- Use constructor injection for related objects.
- Validate all required arguments.
- Defensively copy the product collection.
- Create and display a complete order.
Common Interview Questions
What is a constructor in Java?
A constructor is a special class member invoked during object creation to initialize a new object.
Does a constructor have a return type?
No. A constructor has no return type, not even void.
What is a default constructor?
It is the no-argument constructor automatically provided by the compiler when a class declares no constructor.
What is a no-argument constructor?
It is any constructor that accepts zero arguments and may be explicitly written by the programmer.
Can constructors be overloaded?
Yes. A class can declare multiple constructors with different parameter lists.
Can constructors be overridden?
No. Constructors are not inherited and therefore cannot be overridden.
What does this() do?
It invokes another constructor in the same class.
What does super() do?
It invokes a constructor of the immediate parent class.
Can this() and super() be used in the same constructor?
They cannot both be explicit constructor invocations because each must be the first statement.
What happens if no constructor is written?
The compiler provides a default constructor if the class declares no constructor.
What happens after a parameterized constructor is declared?
The compiler no longer provides an automatic default constructor.
Can a constructor be private?
Yes. A private constructor restricts direct object creation from outside the class.
Can a constructor throw an exception?
Yes. Constructors can throw exceptions when object initialization cannot complete successfully.
Are constructors inherited?
No. Child classes declare their own constructors.
Which constructor executes first in inheritance?
Parent initialization occurs before the child constructor body.
What is constructor chaining?
It is the process of one constructor invoking another constructor using this() or super().
What is constructor injection?
It is supplying required dependencies through constructor parameters.
What is a copy constructor in Java?
It is a programmer-defined constructor pattern that accepts another object of the same type and copies its state.
What is the difference between shallow and deep copying?
A shallow copy may share nested mutable objects, while a deep copy creates independent copies of relevant nested mutable state.
Can an abstract class have a constructor?
Yes. Its constructor initializes the abstract class portion of subclass objects.
Can an interface have a constructor?
No. Interfaces are not instantiated and do not have constructors.
Frequently Asked Questions
Is a constructor mandatory?
Every class has a construction path, but you do not always need to write a constructor because the compiler may provide a default constructor.
Can I call a constructor directly like a method?
Constructors are invoked as part of object creation or through explicit constructor invocation using this() or super().
Can a constructor return this?
A constructor cannot explicitly return an object value.
Can a constructor contain return?
A bare return statement may be used to exit a constructor, but a constructor cannot return a value.
Can constructors be static?
No. Constructors initialize instances, while static members belong to the class.
Can constructors be final?
No. Constructors are not inherited or overridden, so final is not applicable.
Can constructors be abstract?
No. Constructors must contain executable initialization behavior.
Can constructors be synchronized?
The synchronized modifier cannot be applied to a constructor declaration.
Should every field be a constructor parameter?
No. Required state usually belongs in constructors, while optional or derived state may use defaults or other design approaches.
What should I learn after Constructors?
The next lesson covers Packages in Java.
Key Takeaways
- A constructor is a special class member.
- A constructor initializes a new object.
- A constructor has the same name as its class.
- A constructor has no return type.
- A constructor runs during object creation.
- Constructors can receive parameters.
- Constructors can be overloaded.
- Constructors are not inherited.
- Constructors cannot be overridden.
- Constructors cannot be static.
- Constructors cannot be final.
- Constructors cannot be abstract.
- The compiler provides a default constructor only when no constructor is declared.
- A default constructor has no parameters.
- A programmer can explicitly declare a no-argument constructor.
- A no-argument constructor is not always a compiler-provided default constructor.
- Parameterized constructors receive initialization data.
- Constructors help create valid object state.
- The this keyword refers to the current object.
- this.field distinguishes a field from a same-named parameter.
- Constructor overloading provides multiple creation paths.
- Overloaded constructors must have different parameter lists.
- Parameter names do not affect constructor signatures.
- Ambiguous constructor calls cause compile-time errors.
- Constructor chaining reuses initialization logic.
- this() calls another constructor in the same class.
- this() must be the first statement.
- super() calls a parent constructor.
- super() must be the first statement.
- this() and super() cannot both be explicit first statements.
- Recursive constructor invocation is illegal.
- Java inserts implicit super() when appropriate.
- Parent initialization happens before the child constructor body.
- Constructors can use access modifiers.
- Public constructors allow broad direct creation.
- Protected constructors support controlled subclass and package access.
- Package-private constructors restrict creation to the package.
- Private constructors prevent outside direct creation.
- Private constructors support controlled creation patterns.
- Constructors can validate arguments.
- Constructors can reject invalid state.
- Constructors can throw exceptions.
- Failed construction does not produce a normally available object.
- Mutable constructor arguments may create shared state.
- Defensive copying can protect internal state.
- Final instance fields can be initialized in constructors.
- Static fields belong to the class.
- Instance fields belong to individual objects.
- Field initializers run before the constructor body.
- Instance initialization blocks run before the constructor body.
- Static initialization is different from object construction.
- Constructors can receive object references.
- Constructors can create nested objects.
- Constructor injection supplies required dependencies.
- Constructor injection makes dependencies explicit.
- Java has no special built-in copy constructor syntax.
- A class can implement the copy constructor pattern.
- Shallow copies may share nested mutable objects.
- Deep copies create independent relevant nested state.
- Arrays can be copied during construction.
- Collections can be defensively copied.
- Generic classes can have constructors.
- Abstract classes can have constructors.
- Interfaces cannot have constructors.
- Enums can have constructors.
- Records have canonical constructors.
- Records can use compact constructors.
- Constructor references use ClassName::new.
- Factory methods can provide named creation paths.
- Builders can help with many optional parameters.
- Too many overloaded constructors can create the telescoping constructor problem.
- Good constructors create valid objects.
- Good constructors clearly identify required state.
- Good constructors avoid duplicated initialization logic.
- Good constructors validate important values.
- Good constructors manage mutable input carefully.
- Good constructors keep dependencies explicit.
- Good constructors remain focused on initialization.
- Understanding constructors is essential for packages, encapsulation, inheritance, dependency injection, Spring, and enterprise Java development.
Summary
Constructors are special class members that participate in object creation and initialize new objects. They have the same name as their class, have no return type, and execute when objects are created.
When a class declares no constructor, the compiler may provide a default no-argument constructor. Once any constructor is explicitly declared, the compiler no longer provides that automatic default constructor.
Parameterized constructors allow required values to be supplied during object creation. The this keyword is commonly used to distinguish instance fields from same-named constructor parameters.
Constructor overloading provides multiple valid ways to create objects. Constructor chaining with this() allows these construction paths to reuse one primary initialization implementation and avoid duplicate code.
In inheritance, super() invokes a parent constructor. Parent initialization occurs before the child constructor body, and constructors themselves are not inherited or overridden.
Constructors can use access modifiers to control object creation. Public constructors allow general creation, while private constructors can support factories, utility classes, and other controlled creation patterns.
Well-designed constructors validate required data, initialize final fields, prevent invalid objects, handle mutable arguments carefully, and make required dependencies explicit through constructor injection.
Understanding constructors is essential because every useful Java object must be initialized correctly. Constructors form the foundation for encapsulation, inheritance, immutable objects, dependency injection, Spring components, and larger object-oriented applications.
- You understand what a constructor is.
- You understand why constructors are needed.
- You know constructor syntax.
- You know constructor characteristics.
- You can distinguish constructors from methods.
- You understand when constructors execute.
- You understand the object creation process.
- You understand default constructors.
- You understand compiler-provided constructors.
- You understand no-argument constructors.
- You know the difference between default and no-argument constructors.
- You can create parameterized constructors.
- You can initialize fields through constructors.
- You understand the this keyword.
- You understand parameter shadowing.
- You can overload constructors.
- You understand why constructor overloading is useful.
- You know constructor overloading rules.
- You understand constructor signatures.
- You understand ambiguous constructor calls.
- You understand constructor chaining.
- You can use this().
- You know the rules of this().
- You can avoid duplicate initialization logic.
- You understand constructor chaining flow.
- You understand recursive constructor invocation errors.
- You can use super().
- You understand implicit super().
- You can call parameterized parent constructors.
- You know the difference between this() and super().
- You understand constructor execution in inheritance.
- You understand that constructors are not inherited.
- You understand constructor access modifiers.
- You understand public constructors.
- You understand protected constructors.
- You understand package-private constructors.
- You understand private constructors.
- You know private constructor use cases.
- You can design multiple construction paths.
- You can validate constructor arguments.
- You can prevent invalid objects.
- You understand constructor exceptions.
- You understand defensive copying.
- You understand mutable constructor arguments.
- You can initialize final fields.
- You understand constructors and static fields.
- You understand constructors and instance fields.
- You understand field initialization order.
- You understand instance initialization blocks.
- You understand object initialization order.
- You understand static initialization order.
- You understand the complete initialization sequence.
- You understand object references in constructors.
- You can pass objects to constructors.
- You can create nested objects.
- You understand constructor injection.
- You understand the dependency injection concept.
- You understand the copy constructor pattern.
- You understand shallow copy constructors.
- You understand deep copy constructors.
- You can use arrays in constructors.
- You can use varargs constructors.
- You understand collection constructor arguments.
- You understand constructors in generic classes.
- You understand abstract class constructors.
- You understand why interfaces have no constructors.
- You understand enum constructors.
- You understand record constructors.
- You understand canonical record constructors.
- You understand compact record constructors.
- You understand constructor references.
- You understand factory methods versus constructors.
- You understand builders versus constructors.
- You understand the telescoping constructor problem.
- You can design Student constructors.
- You can design BankAccount constructors.
- You can design Product constructors.
- You can design Employee constructors.
- You can design Order constructors.
- You can design immutable objects.
- You understand the constructor design process.
- You can identify common constructor errors.
- You know constructor best practices.
- You are ready to learn Packages in Java.