Constructors in Java
A constructor runs once when an object is created, and its job is to leave that object in a valid state.
-
Java Basics
- Introduction to Java
- Setting Up Java and Writing Your First Program
- Variables, Data Types and Literals in Java
- Type Casting and Type Conversion in Java
- Operators and Expressions in Java
- Input and Output in Java
- Comments, Keywords and Naming Conventions in Java
- Control Flow in Java: if, else and switch
- Loops in Java: for, while and do-while
- Methods
- Arrays and Strings
-
OOP
- Classes and Objects in Java
- Constructors in Java
- The this Keyword in Java
- The static Keyword in Java
- Encapsulation in Java
- Access Modifiers in Java
- Inheritance in Java
- Method Overriding and super in Java
- Polymorphism in Java
- Abstraction, Abstract Classes and Interfaces in Java
- Composition, Aggregation and Association in Java
- The Object Lifecycle in Java
- Core Java
- Exception Handling
-
Collections
- The Java Collections Framework
- List in Java: ArrayList, LinkedList, Vector and Stack
- Set in Java: HashSet, LinkedHashSet and TreeSet
- Map in Java: HashMap, LinkedHashMap and TreeMap
- How HashMap Works Internally in Java
- Queue and Deque in Java: ArrayDeque and PriorityQueue
- Iterators in Java
- Comparable and Comparator in Java
- Collections Utilities and Choosing the Right Collection
- Generics
- Java 8+
- Stream API
- Date and Time
- File and I/O
-
Multithreading
- Threads in Java: Processes, Runnable and Thread
- Thread Lifecycle in Java
- Synchronization in Java: synchronized and volatile
- Locks and Atomic Classes in Java
- Race Conditions and Deadlocks in Java
- The Executor Framework and Thread Pools in Java
- Future and CompletableFuture in Java
- Concurrent Collections in Java
- The Java Memory Model
- JVM and Memory
- Advanced Java
- Networking
- JDBC
- Testing
Definition
A constructor is a special member that initialises a new object. It has the same name as the class, declares no return type, and cannot be called like an ordinary method.
Why constructors exist
Without one, an object would come into existence half built and every caller would have to remember which setters to call in which order. A constructor makes the valid state a precondition rather than a hope.
Syntax
public class Employee {
private final String name;
private final String department;
private double salary;
public Employee(String name, String department, double salary) {
if (salary < 0) {
throw new IllegalArgumentException("Salary must not be negative");
}
this.name = name;
this.department = department;
this.salary = salary;
}
}The default constructor
public class Note {
private String title;
}
Note n = new Note(); // works: the compiler supplied a no argument constructorIf a class declares no constructor at all, the compiler adds a public no argument one. The moment you declare any constructor, that free one disappears.
public class Note {
private String title;
public Note(String title) {
this.title = title;
}
}
// Note n = new Note(); // compile error: no such constructor any moreOverloading constructors
public class Rectangle {
private final double width;
private final double height;
public Rectangle(double side) {
this(side, side); // delegates, must be the first statement
}
public Rectangle(double width, double height) {
if (width <= 0 || height <= 0) {
throw new IllegalArgumentException("Sides must be positive");
}
this.width = width;
this.height = height;
}
}Chaining with this(...) keeps validation and assignment in a single place. Only one such call is allowed, and it must come first.
Constructors and inheritance
class Vehicle {
private final String registration;
Vehicle(String registration) {
this.registration = registration;
}
}
class Taxi extends Vehicle {
private final int seats;
Taxi(String registration, int seats) {
super(registration); // must be the first statement
this.seats = seats;
}
}A subclass constructor always runs a superclass constructor first. If you do not write super(...), the compiler inserts super(). When the superclass has no no argument constructor, the omission becomes a compile error, which is a frequent source of confusion.
Order of initialisation
- Static fields and static blocks, once, when the class is first loaded.
- The superclass constructor.
- Instance field initialisers and instance initialiser blocks, in source order.
- The body of this constructor.
public class Demo {
static { System.out.println("1 static block"); }
{ System.out.println("3 instance block"); }
private int value = report("2 field initialiser runs with the block, in order");
public Demo() {
System.out.println("4 constructor body");
}
private static int report(String message) {
System.out.println(message);
return 0;
}
}Private constructors
public final class MathUtils {
private MathUtils() {
throw new AssertionError("Utility class, do not instantiate");
}
public static int square(int n) {
return n * n;
}
}A private constructor prevents instantiation. It is used for utility classes, for singletons, and for classes that expose static factory methods instead.
Static factory methods as an alternative
public final class Temperature {
private final double celsius;
private Temperature(double celsius) {
this.celsius = celsius;
}
public static Temperature ofCelsius(double value) {
return new Temperature(value);
}
public static Temperature ofFahrenheit(double value) {
return new Temperature((value - 32) * 5 / 9);
}
}Two constructors taking a single double would be impossible to distinguish. Named factory methods solve that, can return a cached instance, and can return a subtype. They are worth reaching for whenever a constructor name would not explain itself.Constructor compared with method
| Aspect | Constructor | Method |
|---|---|---|
| Name | Exactly the class name | Any valid identifier |
| Return type | None, not even void | Required |
| Called | Only through new, this(...) or super(...) | Directly, any number of times |
| Inherited | No | Yes |
Can be static | No | Yes |
Common mistakes
- Writing
public void Employee(...). That is a method that happens to share the class name, and the compiler will not complain untilnew Employee(...)fails. - Adding a constructor and then being surprised that
new X()stops compiling. - Calling an overridable method from a constructor. The subclass override runs before the subclass fields are initialised.
- Doing heavy work such as opening a connection inside a constructor.
- Leaving an object valid only after a setter is called afterwards.
Best practices
- Validate every argument in the constructor and fail immediately with a clear message.
- Assign all
finalfields exactly once. - Let short constructors delegate to the fullest one.
- Prefer a static factory method when the parameter list alone does not explain the intent.
- Keep constructors free of side effects beyond initialisation.
Practice
- Write a
Bookclass with two constructors, one delegating to the other. - Why does adding
Note(String title)break existing code that callednew Note()? - Predict the output of the initialisation order example, then run it.
- Give a
Circleclass a factory methodofDiameterand explain why a second constructor would not work. - Explain the risk of calling an overridable method from a constructor, with a two class example.
Conclusion
A constructor exists to produce a valid object and nothing else. Validate there, assign the final fields there, delegate rather than duplicate, and use a named factory method when a constructor cannot explain itself.