Skip to content
Ameen Abozaid
PublishedBEG-020-L02

Constructors

  • Lesson 2 of 7
  • 11 min read
  • Beginner
On This Lesson 11 topics

Lesson Overview

In the last lesson you built an Account with new Account() and then set each field by hand. That works, but nothing stops you from forgetting to set the number, and then you have an account with null in it. A real account should never exist without an owner.

A constructor solves this. It is a block of code that runs at the moment you use new, so the object is born with the values it needs. In the blueprint picture, the constructor is the building crew's checklist: no house is handed over until every item is ticked.

Why this matters: constructors are how every framework-managed object and every domain object in a backend starts its life. A class whose objects can be created half-empty is a source of null bugs for the entire program.

This lesson builds on Lesson 1 (classes and fields) and on method overloading from the methods module.

Learning Objectives

By the end of this lesson you will be able to:

  • Write a constructor and recognise its shape: the class name, parameters, no return type.
  • Explain what runs when you use new and in which order things happen.
  • Explain the free no-argument constructor, and when it disappears.
  • Write several overloaded constructors for one class.
  • Use this(...) to let one constructor reuse another.
  • Describe what a constructor should and should not do.

Written Explanation

The shape of a constructor

A constructor looks like a method, with two differences: its name is exactly the class name, and it has no return type, not even void.

java
public class Account {
    String owner;
    String number;
    double balance;

    Account(String owner, String number, double balance) {
        this.owner = owner;
        this.number = number;
        this.balance = balance;
    }
}

You do not call a constructor by name. It runs automatically as part of new:

java
Account a = new Account("Ana", "A-1", 120.5);

What happens here, step by step: Java creates a new object with all fields at their defaults (the table from Lesson 1), then runs the constructor body with the arguments you passed, and finally new gives you a reference to the finished object. By the time you can use a, the three fields are set.

A short word about this.owner = owner;. Inside the constructor, owner on the right is the parameter, and this.owner on the left is the field of the object being built. this means "the object I am working on". The next lesson explains this fully, including what goes wrong without it. For now, copy the pattern: this.field = parameter;.

The free no-argument constructor, and when it vanishes

In Lesson 1 you wrote new Account() and it compiled even though the class had no constructor. That is because, as the Java tutorial states, the compiler provides a no-argument default constructor for any class that declares no constructors.

The moment you write any constructor yourself, the free one is gone. With the three-argument constructor above in place, this fails:

java
Account c = new Account();
text
Bad.java:3: error: no suitable constructor found for Account(no arguments)
        Account c = new Account();
                    ^
    constructor Account.Account(String,String,double) is not applicable
      (actual and formal argument lists differ in length)
1 error

This is a feature, not a nuisance. By writing a constructor that requires an owner, you have said "an Account must have an owner", and the compiler now enforces it everywhere. If you still want a no-argument form, write one explicitly.

Overloading constructors

A class may have several constructors, as long as their parameter lists differ in number or types. This is the same overloading you used for methods. Java picks the one that matches the arguments:

java
Account(String owner, String number, double balance) { ... }
Account(String owner, String number) { ... }   // new account, no money yet

new Account("Ana", "A-1", 120.5) uses the first; new Account("Omar", "A-2") uses the second.

Reusing code with this(...)

The second constructor must also set all three fields. Copying the three assignment lines would repeat code, and if the rules ever change, you would have to fix two places. Instead, one constructor can call another in the same class with this(...):

java
Account(String owner, String number) {
    this(owner, number, 0.0);
}

"Set the balance to 0.0, and let the full constructor do the work." The call this(...) has to be the first statement in the constructor. The compiler rejects it elsewhere (JDK 21):

text
Late.java:6: error: call to this must be first statement in constructor
        this(n, 1);
            ^

A good habit: one constructor, usually the one with the most parameters, does the real work; all other constructors forward to it with this(...).

What a constructor should do

A constructor's job is to put the object into a valid starting state. That means assigning fields, and perhaps a quick check of the arguments. It should not do heavy work: reading files, calling a web service, or talking to a database. If that work is slow or fails, new fails with it, and the failure shows up in an unexpected place. Keep constructors short and predictable, and do the heavy work in separate methods that you call deliberately.

Two traps. First, adding a return type turns a constructor into an ordinary method:

java
void Account(String owner) { ... }   // NOT a constructor: a method that happens to be called Account

It compiles, which makes it hard to spot. Second, a constructor cannot be called like a method (a.Account(...) is an error). It only runs through new.

Examples

Example 1: a full constructor and a short one (simple)

java
public class Account {
    String owner;
    String number;
    double balance;

    Account(String owner, String number, double balance) {
        this.owner = owner;
        this.number = number;
        this.balance = balance;
    }

    Account(String owner, String number) {
        this(owner, number, 0.0);
    }
}
java
public class Main {
    public static void main(String[] args) {
        Account a = new Account("Ana", "A-1", 120.5);
        Account b = new Account("Omar", "A-2");
        System.out.println(a.owner + " " + a.balance);
        System.out.println(b.owner + " " + b.balance);
    }
}

Output (JDK 21):

text
Ana 120.5
Omar 0.0

Both accounts have an owner and a number from the moment they exist. Omar's balance comes from the 0.0 that the short constructor passed along.

Example 2: a product with a default stock (practical)

A shop lets staff create a product with or without an initial stock:

java
public class Product {
    String name;
    double price;
    int stock;

    Product(String name, double price, int stock) {
        this.name = name;
        this.price = price;
        this.stock = stock;
    }

    Product(String name, double price) {
        this(name, price, 0);
    }
}
java
Product a = new Product("Tea", 2.5, 40);
Product b = new Product("Mug", 6);
System.out.println(a.name + " " + a.stock + " / " + b.name + " " + b.stock);

Output: Tea 40 / Mug 0. Notice 6 is an int and the second parameter is a double. Java widens it automatically, as in earlier modules, so the call matches the two-parameter constructor.

Example 3: a constructor that checks its input (realistic)

Real objects must not start in an impossible state. A constructor can refuse bad arguments by throwing an exception. This uses if and throw, so treat it as a preview of exceptions, which a later module teaches in full:

java
Account(String owner, String number, double balance) {
    if (owner == null || owner.isBlank()) {
        throw new IllegalArgumentException("owner is required");
    }
    this.owner = owner;
    this.number = number;
    this.balance = balance;
}

If someone writes new Account("", "A-1", 0), the program stops with an error message at the exact place where the bad value came in, rather than carrying a nameless account through the whole program. A quick check like this is acceptable in a constructor; calling a database from it would not be.

Backend Insight

Backend objects are created in huge numbers, and often by code you did not write. A web framework builds a new object for each request; a database tool builds one for each row it reads. They rely on the constructors you provide, so the rules you put there become the rules for every object.

Two practical consequences. First, a constructor that does heavy work makes object creation slow and hard to test, because you cannot create the object without also triggering the file or network call. Second, required data belongs in constructor parameters: "an Order has a customer" is enforced by the compiler if the only constructor asks for one. Later modules will show how frameworks use constructors to hand objects the things they need (dependency injection), but the principle starts with what you wrote today.

Common Mistakes

Mistake 1: Giving the constructor a return type

  • Incorrect: void Account(String owner) { ... }, which is a method, not a constructor.
  • Correct: Account(String owner) { ... }.

If new Account("Ana") complains that no constructor exists even though you wrote one, check for a stray void.

Mistake 2: Expecting the free no-argument constructor after writing your own

Once any constructor exists, new Account() stops compiling, as shown above. Either pass arguments, or write a no-argument constructor on purpose.

Mistake 3: Putting this(...) after other statements

It must be the first statement. Put any checks after the call, or do them in the constructor that does the real work.

Mistake 4: Doing heavy work in the constructor

Opening a file or connecting to a server in a constructor makes new slow and fragile. Keep constructors to assigning fields and quick checks.

Mistake 5: Repeating the assignments in every constructor

If three constructors each copy the same four lines, a fix must be made three times, and someone will forget one. Forward with this(...) instead.

Exercises

Try each exercise yourself before opening the solutions. The exercises continue with Product from the last lesson.

Level A: Foundation

A1. Which of these is a constructor of Product, and why? (a) void Product(String n) { } (b) Product(String n) { } (c) product(String n) { }

Show solution A1

A1. Only (b). A constructor has the class name (with the same capital letter) and no return type. (a) is a method called Product because of the void; (c) is a method too, since the name does not match the class.

A2. Product has only one constructor, Product(String name, double price, int stock). Does new Product("Tea", 2.5) compile? Does new Product()?

Show solution A2

A2. new Product("Tea", 2.5) does not compile, since the only constructor needs three arguments. new Product() does not either: the free no-argument constructor disappeared when the class got its own constructor.

A3. What does this(name, price, 0); do when it appears in a constructor?

Show solution A3

A3. It calls another constructor of the same class (the one that takes a String, a double, and an int), passing the current name and price and a stock of 0. It must be the first statement of the constructor.

Level B: Application

B1. Write a Product class with fields name, price, stock, a constructor with all three, and an overloaded one with only name and price that sets stock to 0 by calling the first. Create two products in main and print their stock.

Show solution B1

B1.

java
public class Product {
    String name;
    double price;
    int stock;

    Product(String name, double price, int stock) {
        this.name = name;
        this.price = price;
        this.stock = stock;
    }

    Product(String name, double price) {
        this(name, price, 0);
    }
}
java
Product a = new Product("Tea", 2.5, 40);
Product b = new Product("Mug", 6);
System.out.println(a.stock + " " + b.stock);   // 40 0

B2. The following constructor looks right but leaves the field unset. Run it mentally and say what new Fix("x").name prints, then fix it.

java
public class Fix {
    String name;
    Fix(String name) { name = name; }
}
Show solution B2

B2. It prints null. Inside the constructor, name is the parameter, so name = name; assigns the parameter to itself and never touches the field, which keeps its default null. (We ran this on JDK 21 and it printed null.) The fix is this.name = name;.

B3. Rewrite this class so that the second constructor does not repeat the first one's work:

java
public class Point {
    int x, y;
    Point(int x, int y) { this.x = x; this.y = y; }
    Point() { this.x = 0; this.y = 0; }
}
Show solution B3

B3.

java
public class Point {
    int x, y;
    Point(int x, int y) { this.x = x; this.y = y; }
    Point() { this(0, 0); }
}

Level C: Challenge

C1. A Member for a library needs a name and a membership number (both required) and an optional age that defaults to 18. Write the class with two constructors so that the required data cannot be left out. Show a new call for each.

Show solution C1

C1.

java
public class Member {
    String name;
    String number;
    int age;

    Member(String name, String number, int age) {
        this.name = name;
        this.number = number;
        this.age = age;
    }

    Member(String name, String number) {
        this(name, number, 18);
    }
}

new Member("Lina", "M-7") gets age 18; new Member("Karim", "M-8", 34) sets it. There is no way to create a member without name and number, because no constructor allows it.

C2. Explain why a constructor that downloads a file from the internet is a bad idea. Give two problems it could cause.

Show solution C2

C2. First, new becomes slow, and it fails if there is no network, so creating a simple object can crash a program for reasons unrelated to the object. Second, tests cannot create the object without actually downloading, so they are slow and unreliable. The download should be a separate method that a caller invokes on purpose.

Mini Challenge

Take the class you wrote for the Mini Challenge in Lesson 1, or pick a new one. Add:

  1. A constructor that takes every field, written with this.field = parameter;.
  2. A second constructor that leaves out at least one field and fills it from a sensible default using this(...).
  3. A main that creates one object through each constructor and prints all fields.

Then deliberately break it: move the this(...) call below a print statement and read the compiler message, then call new with no arguments and read that error too. Put each change back.

Check your work: no assignment lines are duplicated between the constructors, and every object printed has no unexpected null.

Summary & Next Lesson

Key takeaways

  • A constructor has the class name and no return type, and runs automatically when you use new.
  • Java provides a no-argument constructor only if you write no constructor at all; once you write one, the free one is gone.
  • Overloaded constructors have different parameter lists. Forward from the shorter ones to the fullest with this(...), which must be the first statement.
  • Constructors should set fields and make quick checks, never do heavy work.
  • Required data belongs in constructor parameters.

Next lesson

You used this.owner = owner; as a pattern. The next lesson, "Instance methods and this", explains what this really is, writes methods that work on an object's own data, and shows the exact bug that happens when this is forgotten.

Self-check

  1. What two things distinguish a constructor from a method?
  2. When does a class stop having a free no-argument constructor?
  3. What does overloading mean for constructors?
  4. Where must a this(...) call be in a constructor?
  5. Name one thing a constructor should not do.
Show answers
  1. Its name is the class name, and it has no return type.
  2. As soon as you write any constructor yourself.
  3. Several constructors in one class with different parameter lists; Java chooses by the arguments.
  4. It must be the first statement.
  5. Heavy work, such as reading a file or calling a network service or database.

References

  • Oracle, The Java Tutorials, Providing Constructors for Your Classes. Supports: a constructor uses the class name and has no return type; the compiler provides a no-argument constructor only for classes without constructors; constructors can be overloaded.
  • Oracle, The Java Tutorials, Using the this Keyword. Supports: this refers to the current object, a parameter can shadow a field, and this(...) must be the first line of a constructor.
  • The compiler messages, the outputs, and the fact that the name = name; bug prints null come from compiling and running the code on JDK 21. The check in Example 3 was also run and stops with IllegalArgumentException: owner is required; no reference page was consulted for exceptions.

Practice

Apply this lesson with the module's lab, exercises and quiz.

Lab and exercises Quiz

Marking a lesson complete is optional and you can undo it any time. Your progress stays in this browser.

Continue to next lesson

Type to search levels, phases, modules and lessons.