Constructors and Object Lifecycle
How objects are born, live, and die — and how to design constructors that produce only valid objects
A constructor is a special method that runs once, when a new
object is being built with new. Its job is to bring a valid
object into existence. If your constructor finishes without
throwing an exception, callers are entitled to assume the object is
ready to use.
This page is about constructors and the broader lifecycle of an object — birth, life, and eventual death.
Anatomy of a constructor
A constructor:
- Has the same name as the class.
- Has no return type (not even
void). - May take parameters, just like a regular method.
Why constructors exist
In a language without constructors, you would do something like this:
Point p = new Point(); // create a blank
p.x = 3; // fill in fields one by one
p.y = 4;
// hope you didn't forget anyTwo big problems:
- Half-built objects. Between the
newand the last assignment, the object is invalid. If anyone uses it in that window, bad things happen. - Forgotten fields. Nothing forces you to set every field.
Constructors fix both. With new Point(3, 4), an object is born
fully initialized, in one atomic step.
The default constructor
If you do not declare any constructor at all, Java gives you a free one with no parameters that does nothing. The moment you declare any constructor, the default disappears — so if you want a no-arg constructor and a custom one, you have to declare both yourself.
this(1) calls another constructor of the same class. This is
called constructor chaining, and it is the right way to share
setup logic between constructors.
Enforcing invariants in the constructor
The constructor is the perfect place to enforce the rules that must always be true about an object. If the rules can't be met, throw an exception rather than silently creating a broken object.
A Person with age = -5 simply never comes into existence. The
caller learns about the problem immediately, at the call site, not
hours later when something downstream blows up.
The lifecycle of an object
- Allocation.
new Point(3, 4)finds free memory on the heap. - Default initialization. Java fills every field with its
default value (
0for numbers,falsefor booleans,nullfor references). - Constructor. The constructor body runs.
- Life. The object is now usable. Its fields can change
(unless declared
final). Methods can be called on it. - Unreachability. When no living variable holds a reference to it, the object becomes garbage.
- Garbage collection. Eventually the JVM's garbage collector notices and reclaims the memory.
Steps 1-3 happen in milliseconds during new. Steps 4-6 happen over
the program's lifetime.
final fields and "immutable" objects
Fields declared final must be assigned exactly once — either
inline or in the constructor — and then never again. An object whose
fields are all final (and which holds only references to other
immutable things) is called immutable. Immutable objects are
remarkably useful:
- They can be shared freely without worrying about who might change them.
- They are easy to reason about.
- They are inherently thread-safe.
String is the most famous immutable class in Java. So are Integer,
LocalDate, and many others.
What happens at the end
Java does not have destructors (the C++ feature for "code that runs
when an object is about to die"). The garbage collector simply
reclaims memory. If your object held a non-memory resource (file
handle, network socket, database connection), you must release it
explicitly, usually with a close() method called inside a
try-with-resources block. We will see that in the exceptions
chapter.
Why do constructors not have a return type?
Because they return void
Because their "return value" is the newly created object, handed back by new, not by the constructor itself
Because Java's syntax does not allow return types in any method
Because they always return null
What is the main purpose of a constructor?
To free memory when an object dies
To run code that brings a new object into a valid initial state
To replace one object with another
To print debug information
A small challenge
Create a class Range representing the half-open interval [low, high).
Requirements:
- Constructor
Range(int low, int high). - The constructor must throw
IllegalArgumentExceptioniflow >= high. - Both
lowandhighshould beprivate finalfields. - Method
int low()returns low. Methodint high()returns high. - Method
int length()returnshigh - low. - Method
boolean contains(int n)returns true iflow <= n < high.
Main.java is fixed and must print exactly:
length=5
contains 3 = true
contains 5 = false
rejected: low must be less than high
Constructors are the front gate of every class you'll ever write. They are where you decide what kinds of objects can exist. Next we look at one of OOP's most powerful — and most over-used — relationships: inheritance and polymorphism.