Let's build the concept from zero → object creation → constructor execution → this → shadowing → constructor types → overloading → common traps.
Suppose we create a Student object:
Student s = new Student();The object exists, but how do we give it meaningful initial values?
Without a constructor:
class Student {
int id;
String name;
public static void main(String[] args) {
Student s = new Student();
s.id = 101;
s.name = "Ravi";
System.out.println(s.id + " " + s.name);
}
}We have to:
- Create the object.
- Access the fields.
- Assign values separately.
A constructor allows initialization to happen as part of object creation.
class Student {
int id;
String name;
Student(int id, String name) {
this.id = id;
this.name = name;
}
public static void main(String[] args) {
Student s = new Student(101, "Ravi");
System.out.println(s.id + " " + s.name);
}
}Output:
101 Ravi
The important idea is:
new Student(101, "Ravi")
↓
Constructor
↓
Object initialized
A constructor is a special member of a class that is invoked when an object is initialized through class-instance creation.
A constructor:
- has the same name as the class
- has no return type
- can accept parameters
- can be overloaded
- is invoked automatically as part of
new - is primarily used to initialize object state
Example:
class Student {
Student() {
System.out.println("Constructor executed");
}
}Creating an object:
Student s = new Student();Output:
Constructor executed
You did not explicitly write:
s.Student();The constructor is invoked as part of:
new Student();This is one of the most common doubts.
Compare:
class Student {
Student() {
System.out.println("Constructor");
}
void Student() {
System.out.println("Method");
}
}The first is a constructor:
Student()The second is a method:
void Student()Why?
Because the second has:
void
A constructor cannot have a return type, not even void.
| Constructor | Method |
|---|---|
| Same name as class | Can have any valid name |
| No return type | Has return type or void |
| Invoked during object creation | Invoked explicitly or through method calls |
| Used mainly for initialization | Used to perform operations |
| Can be overloaded | Can be overloaded |
| Not inherited | Methods can be inherited depending on circumstances |
Consider:
Student s = new Student(101, "Ravi");Don't treat this as one mysterious statement.
Break it down:
Student
↓
reference type
s
↓
reference variable
new
↓
creates a new object
Student(101, "Ravi")
↓
constructor invocation
Conceptually:
Student s
↓
reference variable declared
new Student(...)
↓
new object initialized
=
↓
reference stored in s
For:
Student s = new Student(101, "Ravi");a simplified conceptual flow is:
1. Class information is available
↓
2. Memory is allocated for the object
↓
3. Instance fields initially receive default values
↓
4. Constructor invocation begins
↓
5. Constructor body initializes state
↓
6. Reference to the object is assigned to s
For example:
class Student {
int id;
String name;
Student(int id, String name) {
this.id = id;
this.name = name;
}
}Before constructor assignments, conceptually:
id = 0
name = null
Then:
this.id = id
this.name = name
After construction:
id = 101
name = "Ravi"
Consider:
class Student {
int id;
String name;
}These are instance variables.
They belong to each object.
Suppose:
Student s1 = new Student();
Student s2 = new Student();Conceptually:
s1 object s2 object
┌──────────────┐ ┌──────────────┐
│ id = ... │ │ id = ... │
│ name = ... │ │ name = ... │
└──────────────┘ └──────────────┘
Each object has its own instance state.
Now:
void display() {
int marks = 90;
}marks is a local variable.
It belongs to the execution of display(), not to each object as a field.
| Feature | Instance Variable | Local Variable |
|---|---|---|
| Declared | Inside class, outside methods/constructors | Inside method/constructor/block |
| Belongs to | Object | Method/block execution |
| Default value | Yes | No |
| Must be definitely assigned before reading | No | Yes |
| Scope | Instance context | Declaring method/block |
| Example | int id; |
int x = 10; |
Example:
class Student {
int id; // instance variable
Student(int value) {
int x = 10; // local variable
id = value;
}
}Suppose:
class Demo {
int x;
double price;
boolean flag;
String name;
}If an object is created:
Demo d = new Demo();The fields receive their default values:
int → 0
double → 0.0
boolean → false
reference → null
But local variables are different:
int x;
System.out.println(x);❌ Compile-time error because x has not been definitely assigned.
A constructor with zero parameters:
class Student {
Student() {
System.out.println("Hello");
}
public static void main(String[] args) {
Student s = new Student();
}
}Output:
Hello
More precise terminology:
No-argument constructor means a constructor whose parameter list is empty.
Some textbooks call it a non-parameterized constructor.
A constructor that accepts parameters:
class Student {
int id;
String name;
Student(int id, String name) {
this.id = id;
this.name = name;
}
public static void main(String[] args) {
Student s = new Student(101, "Ravi");
System.out.println(s.id);
System.out.println(s.name);
}
}Output:
101
Ravi
Here:
Student(int id, String name)is a parameterized constructor.
Now we reach a very important concept.
Suppose:
class Student {
int id;
Student(int id) {
id = id;
}
}At first glance, it looks like:
instance id = parameter id
But that's not what happens.
The constructor parameter:
int idhas the same name as the instance field:
int idThe parameter shadows the instance variable within the constructor's scope.
Therefore:
id = id;effectively refers to the parameter on both sides.
The instance field remains unchanged.
class Student {
int id;
String name;
Student(int id, String name) {
id = id;
name = name;
}
public static void main(String[] args) {
Student s = new Student(101, "Ravi");
System.out.println(s.id + " " + s.name);
}
}Output:
0 null
Why?
Because:
id = id;doesn't mean:
object.id = parameter.id
Instead, the local parameter shadows the field.
this is a reference that represents the current object.
So:
this.idmeans:
The
idfield belonging to the current object.
Therefore:
this.id = id;means:
current object's id = constructor parameter id
Similarly:
this.name = name;means:
current object's name = constructor parameter name
class Student {
int id;
String name;
Student(int id, String name) {
this.id = id;
this.name = name;
}
public static void main(String[] args) {
Student s = new Student(101, "Ravi");
System.out.println(s.id + " " + s.name);
}
}Output:
101 Ravi
this.field = parameter;This is extremely common in Java constructors.
Suppose:
Student s1 = new Student(101, "Ravi");Inside the constructor, this refers to the object currently being initialized.
Conceptually:
new Student(...)
↓
current object
↑
this
For another object:
Student s2 = new Student(102, "Priya");during that constructor invocation, this refers to the second object.
So this is not a fixed object.
It refers to the current object for that invocation.
Yes.
For example:
class Student {
int id;
Student(int value) {
this.id = value;
}
}There is no shadowing because the parameter is named value.
Here, this is also valid:
id = value;So this isn't required merely because you're inside a constructor.
It's particularly useful when the parameter and field have the same name.
Just like methods, constructors can be overloaded.
class Student {
int id;
String name;
Student() {
id = 0;
name = "Unknown";
}
Student(int id, String name) {
this.id = id;
this.name = name;
}
}Now there are two constructors:
Student()
Student(int, String)
Java selects the appropriate constructor based on the arguments supplied to new.
new Student();calls:
Student()
while:
new Student(101, "Ravi");calls:
Student(int, String)
Student()
Student(int id)
Student(int id, String name)Same class name + different parameter lists.
display()
display(int x)
display(int x, int y)Same method name + different parameter lists.
The principle is similar.
❌ No.
This is invalid:
class Student {
int Student() {
return 10;
}
}This is not a constructor.
It is a method named Student, because:
intis present.
Correct constructor:
Student() {
}❌ No.
This:
void Student() {
}is a method, not a constructor.
Remember:
Student() → constructor
void Student() → method
❌ No.
Constructors are associated with object initialization, so Java does not allow:
static Student() {
}❌ No.
Constructors cannot be declared final.
❌ No.
An abstract method has no implementation and requires overriding.
A constructor is used for object initialization and isn't inherited/overridden.
Example:
class Demo {
private Demo() {
System.out.println("Private constructor");
}
}A private constructor prevents normal object creation from outside the class:
Demo d = new Demo(); // ❌ outside the classPrivate constructors are useful in patterns such as controlled instantiation and utility-style classes.
Yes.
public Student() {
}Its accessibility depends on the access modifier.
Possible access levels include:
public
protected
package-private (no modifier)
private
Consider:
class Student {
int id;
String name;
}There is no constructor written.
Java can provide a default constructor automatically.
Conceptually:
Student() {
super();
}The exact source code isn't literally inserted into your file, but this is a useful conceptual model.
Then:
Student s = new Student();works.
These terms are often mixed up.
Any constructor with zero parameters:
Student() {
}It may be written by you.
The constructor the compiler provides if you don't declare any constructor.
So:
class Student {
}gets a compiler-provided no-argument constructor.
But:
class Student {
Student() {
}
}has a programmer-written no-argument constructor.
Consider:
class Student {
Student(int id) {
}
}Now:
Student s = new Student();❌ Compile-time error.
Why?
Because once you declare a constructor yourself, the compiler does not additionally provide the default no-argument constructor.
You must explicitly provide one if you want it:
Student() {
}So:
class Student {
Student() {
}
Student(int id) {
}
}Now both are available.
this() is different from this.
Refers to the current object:
this.id = id;Calls another constructor in the same class.
Example:
class Student {
int id;
String name;
Student() {
this(0, "Unknown");
}
Student(int id, String name) {
this.id = id;
this.name = name;
}
}Now:
Student s = new Student();Flow:
Student()
↓
this(0, "Unknown")
↓
Student(int, String)
↓
fields initialized
This is called constructor chaining.
If used inside a constructor:
this(...);must be the first statement in that constructor.
Correct:
Student() {
this(0, "Unknown");
}Incorrect:
Student() {
System.out.println("Hello");
this(0, "Unknown"); // ❌
}Memorize this difference:
this |
this() |
|---|---|
| Refers to current object | Calls another constructor in same class |
Used like this.id |
Used like this(...) |
| Can access instance members | Must be first statement when used in constructor |
| Doesn't invoke a constructor | Invokes another constructor |
There is another important keyword:
super()It invokes a constructor of the superclass.
Example:
class Animal {
Animal() {
System.out.println("Animal constructor");
}
}
class Dog extends Animal {
Dog() {
super();
System.out.println("Dog constructor");
}
}Creating:
Dog d = new Dog();Output:
Animal constructor
Dog constructor
Why?
The superclass part of the object must be initialized as part of construction.
this()
↓
constructor of same class
super()
↓
constructor of superclass
Both, when explicitly used in a constructor, must appear as the first statement.
Therefore you cannot write:
this();
super();in the same constructor.
You must choose the constructor invocation that applies.
❌ No.
Suppose:
class Animal {
Animal() {
}
}
class Dog extends Animal {
}Dog does not inherit Animal() as a constructor.
But when a Dog object is constructed, a superclass constructor is invoked as part of construction.
This distinction is important:
Constructor inherited? → No
Superclass constructor invoked? → Yes
❌ No.
Overriding applies to inherited methods.
Constructors aren't inherited, so they cannot be overridden.
✅ Yes.
Example:
Student()
Student(int id)
Student(int id, String name)Yes.
class Student {
Student() {
display();
}
void display() {
System.out.println("Hello");
}
}However, calling overridable instance methods from constructors can be dangerous in inheritance scenarios because subclass state may not yet be initialized. That's an advanced design concern worth remembering.
Yes.
Using:
this(...)Example:
class Student {
Student() {
this(101);
}
Student(int id) {
System.out.println(id);
}
}❌ No.
This is invalid:
s.Student();Constructors are invoked through object creation expressions such as:
new Student();or through constructor chaining:
this();
super();You can write:
System.out.println(new Student(101, "Ravi").id);Here you don't declare:
Student s;The object is created and its field is accessed immediately.
This is useful for simple cases, but if you need the object multiple times, a reference variable is clearer:
Student s = new Student(101, "Ravi");Suppose your goal is:
101 Ravi
You can initialize through a constructor:
class Student {
int id;
String name;
Student(int id, String name) {
this.id = id;
this.name = name;
}
public static void main(String[] args) {
Student s = new Student(101, "Ravi");
System.out.println(s.id + " " + s.name);
}
}The constructor initializes.
The println() prints.
Don't say the constructor itself is printing the object's fields unless the constructor contains the println().
You can also write:
class Student {
Student(int id, String name) {
System.out.println(id + " " + name);
}
public static void main(String[] args) {
new Student(101, "Ravi");
}
}Output:
101 Ravi
Here the constructor itself performs the printing.
But this is conceptually different from:
Student s = new Student(101, "Ravi");
System.out.println(s.id + " " + s.name);The latter uses the constructor for initialization, which is generally the more important use.
Example:
class Student {
int id;
String name;
int age;
Student(int id, String name) {
this.id = id;
this.name = name;
}
}age isn't explicitly initialized by the constructor.
It still receives its default value:
age = 0
Constructors can do more than simple assignment.
class Student {
int age;
Student(int age) {
if (age >= 0) {
this.age = age;
} else {
this.age = 0;
}
}
}Now object creation and initial validation happen together.
class Employee {
int id;
String name;
double salary;
Employee(int id, String name, double salary) {
this.id = id;
this.name = name;
this.salary = salary;
}
}One constructor initializes three pieces of state.
class Student {
int id;
String name;
Student(int id, String name) {
this.id = id;
this.name = name;
}
public static void main(String[] args) {
Student s1 = new Student(101, "Ravi");
Student s2 = new Student(102, "Priya");
System.out.println(s1.id + " " + s1.name);
System.out.println(s2.id + " " + s2.name);
}
}Output:
101 Ravi
102 Priya
Each constructor invocation initializes a different object.
Consider:
class Student {
int id;
Student(int id) {
this.id = id;
}
}There are actually two different variables named id.
id
/ \
/ \
instance field parameter
↓ ↓
this.id id
So:
this.id = id;means:
object field = parameter
This is why the statement is so common in Java.
A constructor parameter is a local variable of the constructor's invocation, but when teaching Java, it's useful to distinguish:
local variable
parameter
instance variable
Example:
class Student {
int id; // instance variable
Student(int value) { // parameter
int x = 10; // local variable
this.id = value;
}
}All three have different roles.
Here's a program bringing many concepts together:
class Student {
// Instance variables
int id;
String name;
int age;
// No-argument constructor
Student() {
this(0, "Unknown", 0);
}
// Parameterized constructor
Student(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
void display() {
System.out.println(id + " " + name + " " + age);
}
public static void main(String[] args) {
Student s1 = new Student();
Student s2 = new Student(101, "Ravi", 20);
s1.display();
s2.display();
}
}Output:
0 Unknown 0
101 Ravi 20
Notice the complete flow:
new Student()
↓
Student()
↓
this(0, "Unknown", 0)
↓
Student(int, String, int)
↓
this.id = id
this.name = name
this.age = age
┌──────────────────────────────────────┐
│ CONSTRUCTOR │
├──────────────────────────────────────┤
│ Same name as class │
│ No return type │
│ Called during object creation │
│ Used for initialization │
│ Can have parameters │
│ Can be overloaded │
│ Not inherited │
│ Cannot be overridden │
│ Can be private │
│ Cannot be static/final/abstract │
└──────────────────────────────────────┘
Constructor → no return type
Method → return type/void
this.field
↓
current object's field
this(...)
↓
another constructor in same class
this()
↓
same class constructor
super()
↓
superclass constructor
Default constructor
→ compiler-provided when no constructor is declared
No-argument constructor
→ any constructor with zero parameters
Constructor → can overload
Constructor → cannot override
Student() {
}✅ Yes.
void Student() {
}❌ No. It's a method.
int Student() {
return 10;
}❌ No. It's a method.
✅ Yes.
❌ No.
❌ No.
✅ Yes.
❌ No.
✅ Yes.
Example:
this.id = id;✅ Yes:
this(101);❌ No. Each, when used, must be the first statement, so you cannot use both explicitly in the same constructor.
If you remember only this, you can reconstruct most of the topic:
CONSTRUCTOR
│
Same name as the class
│
No return type
│
Called during new
│
Initializes object
│
┌────────────┴────────────┐
↓ ↓
No-argument Parameterized
│ │
Student() Student(int id,...)
│
↓
this.id = id
│
↓
avoids shadowing
A constructor is a special class member with the same name as the class and no return type, invoked as part of object creation to initialize the object's state. Constructors may be overloaded, are not inherited or overridden, and
thisis commonly used to distinguish instance variables from constructor parameters when shadowing occurs.