Constructors
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.
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
}
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.
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:
- It must have the exact same name as the class.
- 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:
new Pet(...)tells Java: "I need a newPetobject."
- Java allocates a chunk of memory for this new object.
- The
Petconstructor runs. "Buddy"is passed intoinitialName,"Dog"intoinitialSpecies, and5intoinitialAge.- Inside the constructor,
nameis set to "Buddy",speciesto "Dog", andageto 5.isAdoptedis set tofalse. - Once the constructor finishes, Java returns a reference (the memory address) to the newly created object, which gets stored in the
myNewDogvariable.
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:0double:0.0boolean: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.
See it in action
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:
- 1Identify the Attributes (Instance Variables)First, we define the state of a
CoffeeOrder. Based on the problem, we need:customerName(aString)drinkType(aString, e.g., "Latte", "Americano")sizeOz(anint)hasMilk(aboolean)
- 2Write the Class and Instance VariablesLet's lay this out in code.
public class CoffeeOrder { String customerName; String drinkType; int sizeOz; boolean hasMilk; // Constructor will go here } - 3Design and Write the ConstructorThe 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.). - 4Putting It All Together and Using ItHere 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
CoffeeOrderobjects, 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:
- 1Define the Class and Instance VariablesThis part is straightforward.
public class Book { String title; String author; int pages; }Crucially, we will not write a constructor.
- 2Create an Object using the Default ConstructorBecause 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(); - 3Analyze the Object's StateWhat are the values of
unknownBook.title,unknownBook.author, andunknownBook.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.titleis aString(an object reference), so its default value isnull.authoris also aString, so its default isnull.pagesis anint, so its default value is0.
If we were to print these values:
System.out.println(unknownBook.title);would printnull.System.out.println(unknownBook.pages);would print0.
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.
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), anddurationInSeconds(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
thiskeyword 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
studentNameslist? 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!
Practice — 8 questions
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.
public class Pet {
// Instance variables define the Pet's state
String name;
String species;
int age;
boolean isAdopted;
}
- 3.4.A: Develop code to declare instance variables for the attributes to be initialized in the body of the constructors of a class.
- 3.4.A.1
- An object's state refers to its attributes and their values at a given time and is defined by instance variables belonging to the object. This defines a has-a relationship between the object and its instance variables.
- 3.4.A.2
- A constructor is used to set the initial state of an object, which should include initial values for all instance variables. When a constructor is called, memory is allocated for the object and the associated object reference is returned. Constructor parameters, if specified, provide data to initialize instance variables.
- 3.4.A.3
- When a mutable object is a constructor parameter, the instance variable should be initialized with a copy of the referenced object. In this way, the instance variable does not hold a reference to the original object, and methods are prevented from modifying the state of the original object.
- 3.4.A.4
- When no constructor is written, Java provides a no-parameter constructor, and the instance variables are set to default values according to the data type of the attribute. This constructor is called the default constructor.
- 3.4.A.5
- The default value for an attribute of type int is 0. The default value of an attribute of type double is 0.0. The default value of an attribute of type boolean is false. The default value of a reference type is null.
classDiagram
class Pet {
-String name
-String species
-int age
+Pet(String name, String species, int age)
+getName() String
+getAge() int
+setAge(int newAge) void
}
Read what Saavi narrates
(Sound of a warm, friendly classroom)
Hey everyone, it's Saavi from Shrutam. Let's talk about one of the most fundamental ideas in programming: creating things.
Imagine you're ordering a custom PC. You don't just say "give me a computer." You choose the processor, the memory, the graphics card. You provide the specs. A constructor in Java is like the factory that takes your specific order and builds that exact PC for you. It's a special method that creates a new object and sets it up with its initial properties, or "state."
So, let's say we have a blueprint for a `CoffeeOrder`. The blueprint says every order has a customer name, a drink type, a size, and whether it has milk. The constructor is what lets us fill that order.
When we write `new CoffeeOrder("Priya", "Chai Latte", 16, true)`, we're calling the constructor. We're telling the factory, "Okay, build me a new coffee order. The name is Priya, the drink is a Chai Latte, it's 16 ounces, and yes, it has milk." The constructor takes those four pieces of information and assigns them to the new object's internal variables. When it's done, we have a complete, ready-to-go `CoffeeOrder` object.
Now, here's a very common mistake I see all the time. When you write a constructor, it must have the exact same name as the class, and—this is the important part—it has no return type. Not even `void`.
If you write `public void CoffeeOrder(...)`, you've accidentally created a regular method that just happens to have the same name as the class. It's not a constructor, and it won't be called when you use the `new` keyword. Your object will be created with default values like zero or null, which is probably not what you wanted. So, remember: no return type on constructors!
Constructors are your power tool for bringing your class blueprints to life. Once you master them, you're well on your way to building complex and interesting programs. You've got this!
`public void Car(...)` defines a regular method named `Car`, not a constructor. The compiler will let you do this, but it won't be called when you use `new`, leading to uninitialized objects.
Ensure the constructor has no return type at all: `public Car(...)`.
The uninitialized variable will retain its default value (`0`, `false`, or `null`). This can lead to unexpected behavior or errors, like `NullPointerException`.
Make a checklist. For every instance variable in your class, make sure it's explicitly assigned a value inside your constructor.
This creates an alias, not a copy. Both the original object and the new object's instance variable point to the *same* object in memory. Changes made through one reference will be visible to the other, which is a major source of bugs.
For mutable objects like `ArrayList`, create a new instance in the constructor: `this.myList = new ArrayList<>(parameterList);`.
Code like `myCar.Car(...)` is invalid. A constructor's only job is to initialize an object *at the moment of creation*. It can only be invoked with the `new` keyword.
If you need to change an object's state after it's been created, write a separate method, often called a "setter" (e.g., `public void setColor(String newColor)`).
The moment you write *any* constructor (e.g., `public Pet(String name)`), the free, no-parameter default constructor that Java provides disappears.
If you write a constructor with parameters but still need a no-parameter option, you must explicitly write it yourself: `public Pet() { ... }`.
In a constructor like `public Pet(String name) { name = name; }`, you are just assigning the parameter `name` to itself. The instance variable `name` is never touched and will keep its default value (`null`).
Use the `this` keyword to differentiate. `this.name` always refers to the instance variable, while `name` refers to the parameter: `this.name = name;`.