Free for students · Ad-free · WCAG 2.1 AA Compliant · Accessibility

Constructors

Lesson ~11 min read 8 MCQs

In simple terms: In simple terms, constructors are special setup instructions that create a new object and give it its initial properties, like building a new toy and painting it for the first time.

Why this matters

Imagine you're building a custom gaming PC online. You don't just get a random box of parts. You go through a checklist: "I want this processor, that graphics card, 32GB of RAM, and a 2TB solid-state drive." You make specific choices.

The website's order form takes your choices and sends them to the factory. The factory then assembles a PC with the exact components you specified. When it's done, you get a fully built, ready-to-use computer that matches your order.

Constructors in Java are like that factory. They take your specifications (as inputs, called parameters) and build a brand new object, setting up its initial state. Without them, we'd just have blueprints (classes) but no way to build the actual objects. Today, we're learning how to be the architects of that factory.

Classes are blueprints, objects are the actual creations.

Concept overview

classDiagram
  class Pet {
    -String name
    -String species
    -int age
    +Pet(String name, String species, int age)
    +getName() String
    +getAge() int
    +setAge(int newAge) void
  }
A class diagram for a Pet object. It shows private instance variables for name, species, and age, and a public constructor that takes all three as parameters, along with getter and setter methods.

Core explanation

When we design a class, like Dog or Car, we're creating a blueprint. But a blueprint isn't a house; it's just the plan for one. To actually create an object (an instance of the class), we need to call its constructor.

What is an Object's "State"?

Before we build, let's talk about what makes one object different from another. If you have two Car objects, one might be a red Ford Mustang and the other a blue Honda Civic. Their differences—color, make, model—are called their attributes or instance variables.

The specific values of these attributes at any given moment (color is "red", make is "Ford") define the object's state.

Think of it this way: a Car class has-a color, it has-a make, and it has-a model. This "has-a" relationship is key. The attributes belong to the object.

public class Pet {
    // Instance variables define the Pet's state
    String name;
    String species;
    int age;
    boolean isAdopted;
}

Here, every Pet object will have its own name, species, age, and isAdopted status.

Each Pet object has its own unique set of attribute values.

The Constructor: Building the Object

A constructor is a special block of code that runs when you use the new keyword. Its job is to set the initial state of the object.

Here are the rules for writing a constructor:

  1. It must have the exact same name as the class.
  2. It has no return type—not even void.

Let's write a constructor for our Pet class. We want to be able to create a new pet and give it a name, species, and age right away.

public class Pet {
    String name;
    String species;
    int age;
    boolean isAdopted;

    // This is the constructor!
    public Pet(String initialName, String initialSpecies, int initialAge) {
        name = initialName;
        species = initialSpecies;
        age = initialAge;
        isAdopted = false; // Let's assume new pets start as not adopted.
    }
}

When you call this constructor, you're providing the data needed to build the object. The constructor takes that data and assigns it to the instance variables.

To use it, we write: Pet myNewDog = new Pet("Buddy", "Dog", 5);

Here's what happens, step-by-step:

  1. new Pet(...) tells Java: "I need a new Pet object."
The call stack when creating a new `Pet` object.
  1. Java allocates a chunk of memory for this new object.
  2. The Pet constructor runs.
  3. "Buddy" is passed into initialName, "Dog" into initialSpecies, and 5 into initialAge.
  4. Inside the constructor, name is set to "Buddy", species to "Dog", and age to 5. isAdopted is set to false.
  5. Once the constructor finishes, Java returns a reference (the memory address) to the newly created object, which gets stored in the myNewDog variable.

Now, myNewDog holds a fully initialized object with a clear, defined state.

What If You Don't Write a Constructor?

What if you have a simple class and don't write a constructor at all?

public class GameCharacter {
    String name;
    int health;
    boolean isPlayer;
}

If you don't provide any constructor, Java provides one for you for free. This is called the default constructor. It takes no parameters and doesn't have any code in its body.

You can use it like this: GameCharacter monster = new GameCharacter();

But wait—if the constructor body is empty, what are the values of name, health, and isPlayer? They are set to their default values:

  • int: 0
  • double: 0.0
  • boolean: false
  • All object reference types (like String): null

So, after new GameCharacter(), the monster object would have name = null, health = 0, and isPlayer = false.

A Tricky Case: Mutable Parameters

This next concept is subtle, but it's critical for writing robust code and it's a favorite for AP exam questions.

Let's say we have a Team class that holds a roster of players. The roster is an ArrayList<String>.

import java.util.ArrayList;

public class Team {
    String teamName;
    ArrayList<String> players;

    // A common, but flawed, constructor
    public Team(String name, ArrayList<String> initialPlayers) {
        this.teamName = name;
        this.players = initialPlayers; // DANGER!
    }
}

Now, let's see why this is a problem.

ArrayList<String> startingRoster = new ArrayList<>();
startingRoster.add("Maya");
startingRoster.add("Carlos");

// Create the team
Team hawks = new Team("Hawks", startingRoster);

// Now, let's change the original list
startingRoster.add("Jordan");

System.out.println(hawks.players); // Output: [Maya, Carlos, Jordan]

Wait, what? We changed startingRoster, not hawks.players directly, but the team's roster changed anyway!

This happened because ArrayList is a mutable object (it can be changed). When we wrote this.players = initialPlayers;, we didn't copy the list. We just made this.players point to the exact same list in memory as startingRoster. This is called aliasing, and it can lead to nasty, hard-to-find bugs.

The fix is to create a copy of the incoming list in the constructor.

// The correct, safe constructor
public Team(String name, ArrayList<String> initialPlayers) {
    this.teamName = name;
    // Create a NEW ArrayList that is a copy of the original
    this.players = new ArrayList<>(initialPlayers);
}

By writing new ArrayList<>(initialPlayers), we are telling Java to build a brand new ArrayList and populate it with all the elements from initialPlayers. Now, this.players and initialPlayers are two separate lists in memory. If the original list changes, our team's roster is safe.

Always remember: when a constructor parameter is a mutable object, don't just assign it—copy it.

Tracing the `Pet` constructor's execution.

See it in action

Java Code

      
      
Call Stack
Stack is empty — waiting for first call.
Step 0 / 0

Worked examples

Let's walk through a couple of examples to make sure these concepts are solid.

Example 1: CoffeeOrder Class

Problem: Create a class to represent a coffee order at a cafe like Starbucks. It should store the customer's name, the type of drink, the size in ounces, and whether it has milk. Write a constructor to initialize a new order.

Solution Walkthrough:

  1. 1
    Identify the Attributes (Instance Variables)
    First, we define the state of a CoffeeOrder. Based on the problem, we need:
    • customerName (a String)
    • drinkType (a String, e.g., "Latte", "Americano")
    • sizeOz (an int)
    • hasMilk (a boolean)
  2. 2
    Write the Class and Instance Variables
    Let's lay this out in code.
    public class CoffeeOrder {
        String customerName;
        String drinkType;
        int sizeOz;
        boolean hasMilk;
        // Constructor will go here
    }
  3. 3
    Design and Write the Constructor
    The constructor needs to accept values for all these attributes so we can create a specific order. The parameters should match the data types of our instance variables.
    public CoffeeOrder(String name, String drink, int size, boolean milk) {
        customerName = name;
        drinkType = drink;
        sizeOz = size;
        hasMilk = milk;
    }

    Notice the constructor has the same name as the class (CoffeeOrder) and no return type. Inside, we take the parameter values (name, drink, etc.) and assign them to the object's instance variables (customerName, drinkType, etc.).

  4. 4
    Putting It All Together and Using It
    Here is the complete class and an example of how to create an object.
    public class CoffeeOrder {
        String customerName;
        String drinkType;
        int sizeOz;
        boolean hasMilk;
    
        public CoffeeOrder(String name, String drink, int size, boolean milk) {
            this.customerName = name; // Using 'this' for clarity
            this.drinkType = drink;
            this.sizeOz = size;
            this.hasMilk = milk;
        }
    }
    
    // In another file (e.g., Main.java)
    CoffeeOrder priyasOrder = new CoffeeOrder("Priya", "Chai Latte", 16, true);
    CoffeeOrder liamsOrder = new CoffeeOrder("Liam", "Black Coffee", 12, false);

    We've now created two distinct CoffeeOrder objects, each with its own state, thanks to our constructor.


Example 2: Book Class with Default Values

Problem: Create a Book class with title, author, and pages. Then, demonstrate what happens when you create a Book object without writing a constructor.

Solution Walkthrough:

  1. 1
    Define the Class and Instance Variables
    This part is straightforward.
    public class Book {
        String title;
        String author;
        int pages;
    }

    Crucially, we will not write a constructor.

  2. 2
    Create an Object using the Default Constructor
    Because we didn't write any constructor, Java gives us the no-parameter default constructor for free. Let's use it.
    // In a main method somewhere
    Book unknownBook = new Book();
  3. 3
    Analyze the Object's State
    What are the values of unknownBook.title, unknownBook.author, and unknownBook.pages? This is where students often get confused. They aren't blank or random; they are set to specific default values based on their type.
    • title is a String (an object reference), so its default value is null.
    • author is also a String, so its default is null.
    • pages is an int, so its default value is 0.

    If we were to print these values: System.out.println(unknownBook.title); would print null. System.out.println(unknownBook.pages); would print 0.

Why this is important: Relying on default values can be risky. A null title or 0 pages might cause errors later in your program (e.g., a NullPointerException). This is why it's almost always better to write your own constructor to ensure every new object starts in a valid, predictable state.

Flow of creating a `CoffeeOrder` object.

Try it yourself

Ready to build some objects? Give these a try.

Problem 1: The Song Class

Create a Java class named Song.

  • It should have three private instance variables: title (String), artist (String), and durationInSeconds (int).
  • Write a constructor that accepts these three values as parameters and initializes the instance variables.
  • Hint: Remember the syntax for a constructor: same name as the class, no return type. Use the this keyword if your parameter names match your instance variable names.

Problem 2: The Classroom Roster

You're designing a Classroom class. It has a teacherName (String) and an ArrayList<String> of studentNames.

  • Write the constructor for this class: public Classroom(String teacher, ArrayList<String> students).
  • Think carefully: What's the potential trap when initializing the studentNames list? How do you avoid it?
  • Hint: Review the section on mutable parameters. You don't want the classroom roster to change if the original list used to create it changes later!
Avoiding the mutable parameter trap with `ArrayList` in constructors.