☕ Java
Default Constructor
A default constructor is a constructor that takes no parameters. If you write a class without any constructor, the Java compiler automatically inserts a no-argument constructor that calls the superclass no-argument constructor and does nothing else. The moment you define any constructor yourself — with or without parameters — the compiler no longer inserts the default constructor. Understanding when the default constructor exists, when it disappears, and what it initialises is fundamental to understanding how Java objects come into existence.
What Is a Constructor?
A constructor is a special method that runs automatically when an object is created with the new keyword. Its purpose is to initialise the new object into a valid, consistent state before any other code can use it. Constructors look like methods but have two distinguishing features: they have the exact same name as the class, and they have no return type — not even void.
Every object creation in Java involves a constructor call. The expression new BankAccount() allocates memory on the heap for a BankAccount object and then immediately calls the BankAccount constructor to set the initial values of its fields. The constructor finishes, and the new keyword expression evaluates to a reference to the now-initialised object.
Constructors are not inherited. A subclass does not inherit its superclass's constructors — it must define its own, and the first statement of every subclass constructor must be a call to a superclass constructor (either explicit with super(...) or implicit if the superclass has a no-argument constructor). This ensures that the superclass portion of the object is always properly initialised before the subclass adds its own initialisation.
Constructors can be overloaded — a class can have multiple constructors with different parameter lists, each providing a different way to create and initialise an object. They can call each other using this(...) to avoid duplicating initialisation logic.
Java
// ── Constructor anatomy: ─────────────────────────────────────────────
public class Person {
// ┌── same name as class
// │ ┌── no return type (not even void)
public Person() {
// constructor body — initialise fields here
}
}
// ── Constructor is called by new: ────────────────────────────────────
Person p = new Person();
// ↑ ↑
// new calls the no-arg constructor
// allocates memory on the heap
// ── Constructor vs method — key differences: ─────────────────────────
public class Example {
// CONSTRUCTOR — same name as class, no return type:
public Example() {
System.out.println("Constructor called");
}
// METHOD — different name, has return type (void here):
public void example() {
System.out.println("Method called");
}
// COMPILE ERROR — looks like a constructor but has return type:
// public void Example() { } // this is actually a method, not a constructor!
}
// ── What constructors can and cannot do: ─────────────────────────────
public class Account {
private String id;
private double balance;
public Account() {
this.id = generateId(); // ✓ call methods
this.balance = 0.0; // ✓ initialise fields
System.out.println("Account created: " + id); // ✓ print
// return this; // ✗ cannot return a value
// return; // ✓ bare return is allowed (early exit)
}
private String generateId() {
return "ACC-" + System.currentTimeMillis();
}
}The Compiler-Provided Default Constructor
When you write a class with no constructors at all, the Java compiler silently inserts a public no-argument constructor on your behalf. This compiler-generated constructor is called the default constructor. It has no parameters, its body is empty, and its only action is to invoke the superclass no-argument constructor via an implicit super() call.
The default constructor exists purely as a convenience so that simple classes can be instantiated without forcing you to write boilerplate. For a simple data holder or utility class with no initialisation needs, the compiler-generated default constructor is perfectly sufficient.
The critical rule to memorise is: the compiler only inserts the default constructor if the class has zero constructors defined by the programmer. The moment you write any constructor — even one with parameters — the compiler stops generating the default constructor. This catches many beginners by surprise: they write a parameterised constructor, and then find that the no-argument instantiation they had been using stops compiling.
This behaviour is intentional. If you write a parameterised constructor for Product(String name, double price), the compiler assumes you always want a name and price when creating a Product. It does not presume that Product() with no arguments should also be valid — that is your design decision to make explicitly.
Java
// ── No constructor defined — compiler inserts default: ───────────────
public class Box {
private double width;
private double height;
private double depth;
// No constructor written — compiler inserts:
// public Box() { super(); }
}
Box box = new Box(); // works — uses compiler-generated default constructor
// box.width = 0.0 (default value for double)
// box.height = 0.0
// box.depth = 0.0
// ── The default constructor disappears when you add any constructor: ──
public class Product {
private String name;
private double price;
// Adding this constructor REMOVES the compiler-generated default:
public Product(String name, double price) {
this.name = name;
this.price = price;
}
}
// Product p1 = new Product(); // COMPILE ERROR — no default constructor
Product p2 = new Product("Laptop", 999.99); // ✓ only this form works now
// ── To restore the no-arg form, define it explicitly: ─────────────────
public class Product {
private String name;
private double price;
public Product() {
this.name = "Unknown";
this.price = 0.0;
}
public Product(String name, double price) {
this.name = name;
this.price = price;
}
}
Product p1 = new Product(); // ✓ uses explicit no-arg
Product p2 = new Product("Laptop", 999.99); // ✓ uses parameterised
// ── When the default constructor is important: ────────────────────────
// Many frameworks (Hibernate, Jackson, Spring) require a no-arg
// constructor to instantiate objects reflectively.
// If you add a parameterised constructor, you must ALSO explicitly
// write the no-arg constructor for these frameworks to work.
// ── Visibility of the default constructor mirrors the class: ──────────
public class A { } // compiler inserts: public A() { super(); }
class B { } // compiler inserts: (package-private) B() { super(); }
// private class C { } // compiler inserts: private C() { super(); }Default Field Values
When an object is created, all its instance fields are initialised to their default values before the constructor body runs. This happens automatically — you do not need to explicitly set fields to zero or null if those are the values you want. Understanding default values prevents confusion when reading unset fields and explains why Java programs do not contain garbage values the way C programs can.
The default values reflect the type system: numeric types default to zero in their respective type (int is 0, double is 0.0), boolean defaults to false, char defaults to the null character (unicode 0), and all reference types default to null. Local variables inside methods do NOT get default values — reading an uninitialised local variable is a compile error. Only instance fields and static fields get automatic defaults.
Java
// ── Default field values for all types: ──────────────────────────────
public class DefaultValues {
// These fields are automatically initialised to defaults:
byte b; // 0
short s; // 0
int i; // 0
long l; // 0L
float f; // 0.0f
double d; // 0.0
boolean flag; // false
char c; // ' ' (null character)
String str; // null
int[] arr; // null (the array reference, not the array contents)
Object obj; // null
}
public static void main(String[] args) {
DefaultValues dv = new DefaultValues();
System.out.println("int: " + dv.i); // 0
System.out.println("double: " + dv.d); // 0.0
System.out.println("boolean: " + dv.flag); // false
System.out.println("char: " + (int)dv.c);// 0
System.out.println("String: " + dv.str); // null
System.out.println("array: " + dv.arr); // null
}
// ── Instance fields get defaults — local variables do NOT: ────────────
public void demonstrate() {
int localVar;
// System.out.println(localVar); // COMPILE ERROR — variable not initialised
// Local variables must be explicitly initialised before use.
int initialised = 0;
System.out.println(initialised); // 0 — fine, explicitly set
}
// ── Default values are set BEFORE constructor body runs: ──────────────
public class Counter {
private int count; // default: 0
private String label; // default: null
public Counter() {
// count is already 0 here — no need to write: this.count = 0;
this.label = "default"; // explicitly set — null is not useful here
System.out.println("count before set: " + count); // 0
}
}
// ── Danger: null default for reference types: ─────────────────────────
public class Order {
private List<String> items; // null by default — NOT an empty list!
public Order() {
// Without initialisation, items is null.
// Calling items.add("x") would throw NullPointerException.
this.items = new ArrayList<>(); // initialise to empty list in constructor
}
}Writing an Explicit No-Arg Constructor
An explicit no-arg constructor is one you write yourself with no parameters. Unlike the compiler-generated default constructor which has an empty body, your explicit no-arg constructor can contain meaningful initialisation logic — setting fields to sensible starting values, logging object creation, registering the object with a registry, or establishing connections.
The explicit no-arg constructor is necessary whenever you want both a no-argument creation path and a parameterised creation path. It is also required when a superclass does not have a no-arg constructor — in that case every subclass constructor must call a specific superclass constructor with super(...), and the no-arg form of the subclass must also make this call.
Best practice is to ensure that the no-arg constructor always creates the object in a fully valid, usable state. A common mistake is writing a no-arg constructor that leaves required fields as null, creating an object that will throw NullPointerException the first time any of those fields are used. If a field is genuinely required to create a valid object, it should be part of a parameterised constructor — not silently left as null by the no-arg constructor.
Java
// ── Explicit no-arg constructor with meaningful initialisation: ───────
public class ShoppingCart {
private List<Item> items;
private String sessionId;
private LocalDateTime createdAt;
private double discount;
// Explicit no-arg — sets everything to a valid starting state:
public ShoppingCart() {
this.items = new ArrayList<>(); // empty, not null
this.sessionId = UUID.randomUUID().toString();
this.createdAt = LocalDateTime.now();
this.discount = 0.0;
System.out.println("Cart created: " + sessionId);
}
public void addItem(Item item) {
items.add(item); // safe — items is never null
}
public double getTotal() {
double total = items.stream()
.mapToDouble(Item::getPrice)
.sum();
return total * (1 - discount);
}
}
ShoppingCart cart = new ShoppingCart();
// cart.items is an ArrayList — never null
// cart.sessionId is a UUID string
// cart.createdAt is the current time
// ── No-arg constructor required for framework reflection: ─────────────
// Hibernate (JPA) needs to instantiate entities via reflection.
// Jackson needs to deserialise JSON into POJOs.
// Spring needs to instantiate beans.
// All of these require a no-arg constructor.
@Entity
public class UserEntity {
@Id
private Long id;
private String email;
// Required by JPA — without this, Hibernate cannot instantiate:
public UserEntity() { }
// Also provide a useful constructor for application code:
public UserEntity(String email) {
this.email = email;
}
}
// ── Explicit no-arg calling superclass: ───────────────────────────────
public class Animal {
private String species;
public Animal(String species) { // only parameterised constructor
this.species = species;
}
}
public class Dog extends Animal {
private String name;
public Dog() {
super("Canis lupus familiaris"); // must call superclass constructor
this.name = "Unknown";
}
public Dog(String name) {
super("Canis lupus familiaris"); // required — Animal has no no-arg ctor
this.name = name;
}
}Related Topics in Object-Oriented Programming
OOP Concepts
Object-Oriented Programming (OOP) is a programming paradigm that organises software around objects — self-contained units that combine data (fields) and behaviour (methods). Java is a class-based, object-oriented language where almost everything is an object. OOP provides four foundational principles: encapsulation, inheritance, polymorphism, and abstraction. Together they produce software that is modular, reusable, maintainable, and easier to reason about as systems grow in complexity.
Classes
A class is the fundamental building block of Java. It is a blueprint that defines the structure and behaviour of objects — what data each object holds (fields) and what operations it can perform (methods). Every Java program is composed of classes. Understanding how to design a class well — choosing the right access modifiers, separating state from behaviour, and writing cohesive single-responsibility classes — is the foundation of object-oriented programming in Java. This entry covers class anatomy, fields, methods, access modifiers, static vs instance members, the this keyword, and class design principles.
Objects
An object is a runtime instance of a class. Where a class is a blueprint that exists in source code, an object is a living entity that exists in memory during program execution — it has its own identity, its own state stored in its fields, and the ability to respond to method calls. Every object has three fundamental properties: identity (a unique memory address), state (the current values of its fields), and behaviour (the methods it responds to). This entry covers object identity vs equality, the Object class hierarchy, object state and mutation, method calls, toString, equals and hashCode, and the object lifecycle.
Object Creation
Object creation in Java is the process of allocating memory, initialising fields, and running constructor logic to bring an object into existence. The new keyword is the primary mechanism, but Java also provides factory methods, builder patterns, copy constructors, and object cloning. Constructors are special methods that set up the initial state — their design determines how easy or difficult the class is to use correctly. This entry covers constructors in depth, constructor overloading and chaining, copy constructors, factory methods, the builder pattern, and the difference between shallow and deep copy.