Constructors
A constructor is a special block that runs when an object is created with new. Its job is to bring an object into a fully valid initial state. A constructor shares the class’s name, declares no return type (not even void), and may be overloaded to support different ways of building the same type.
Default vs Parameterized Constructors
If you write no constructor at all, the compiler synthesizes a default constructor: a public, no-argument constructor that simply calls super().
public class User {
String name;
}
// Compiler provides: public User() { super(); }
A parameterized constructor accepts arguments to initialize fields explicitly.
public class User {
private final String name;
private final String email;
public User(String name, String email) {
this.name = name;
this.email = email;
}
}
Warning: The moment you declare any constructor, the compiler stops generating the default one. If you still need a no-arg constructor, you must write it yourself.
The No-Arg Constructor Rule
Frameworks like Jackson, JPA, and many DI containers instantiate objects reflectively and often require a no-argument constructor. If your class has only parameterized constructors, add an explicit no-arg one to stay compatible.
public User() {
this("anonymous", "none@example.com"); // delegate via this()
}
Constructor Overloading
You may declare multiple constructors that differ in parameter list. The compiler selects the right one based on the arguments supplied at the call site.
public class Rectangle {
private final int width, height;
public Rectangle(int side) { this(side, side); }
public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
}
this() Chaining
this(...) calls another constructor in the same class, centralizing initialization logic so it lives in one place. When present, it must be the first statement in the constructor.
public class Server {
private final String host;
private final int port;
public Server() { this("localhost"); }
public Server(String host) { this(host, 8080); }
public Server(String host, int port) {
this.host = host;
this.port = port;
}
}
System.out.println(new Server().port);
Output:
8080
super() and the Superclass
super(...) invokes a constructor of the parent class and, like this(), must be the first statement. If you write neither this() nor super(), the compiler inserts an implicit super() call to the parent’s no-arg constructor.
public class Animal {
Animal(String name) { /* ... */ }
}
public class Dog extends Animal {
Dog() {
super("dog"); // required: Animal has no no-arg constructor
}
}
Note: A constructor cannot use both
this()andsuper()—each can be the first statement, but only one can occupy that slot. Chains ofthis()eventually bottom out in a constructor that (implicitly or explicitly) callssuper().
Copy Constructors
Java has no built-in copy constructor, but you can write one to create a new object from an existing instance. This is a clean, explicit alternative to Cloneable/clone().
public class Point {
private final int x, y;
public Point(int x, int y) { this.x = x; this.y = y; }
// Copy constructor
public Point(Point other) {
this(other.x, other.y);
}
}
Tip: For deep copies, copy mutable reference fields rather than aliasing them, so the original and the copy stay independent.
Best Practices
- Validate arguments in the constructor; reject invalid state early with
IllegalArgumentException. - Centralize logic with
this()chaining instead of duplicating field assignments. - Keep constructors cheap—no blocking I/O or heavy work; favor factory methods for that.
- Make fields
finaland assign them once in the constructor for safe immutability. - Add an explicit no-arg constructor when a framework requires it.
- Prefer static factory methods or the Builder pattern when a class has many optional parameters.