Unit 3.4: Constructors
Constructors are special methods used to "build" and initialize new objects. They bridge the gap between a class (the blueprint) and an object (the individual instance).
Object State and the "has-a" Relationship
An object's state refers to the data it holds at any given time. This data is stored in instance variables.
- The state of an object is defined by the current values of its instance variables. A constructor's primary responsibility is to establish the object's initial state.
- "has-a" relationship: We say a class "has-a" set of variables. This relationship extends to other objects as well. For example, a
Carclass might have anEngineobject as one of its attributes.
// The "Component" class
public class Engine {
private int horsepower;
}
// The "Container" class
public class Car {
// The Car "has-a" color (String)
private String color;
// The Car also "has-an" Engine (Object)
private Engine myEngine;
}
Basic Example: The Point Class
We will use the following Point class to illustrate how constructors work in the sections below.
public class Point {
private int x;
private int y;
// 1. Parameterized constructor (You provide the values)
public Point(int xVal, int yVal) {
x = xVal;
y = yVal;
}
// 2. No-argument constructor (Uses fixed starting values)
public Point() {
x = 0;
y = 0;
}
// Accessors needed for examples
public int getX() { return x; }
public int getY() { return y; }
public void setX(int newX) { x = newX; }
}
Memory Allocation and Initialization
When you use the new keyword to call a constructor, three distinct steps occur:
- Instantiation is the process of creating a concrete object from a class blueprint. The
newkeyword triggers the JVM to find an empty spot in RAM, carve out enough space for all instance variables, and then run the constructor to fill them in.
- Memory is allocated for the new object.
- The initial state is set (instance variables are initialized).
- The object reference (memory address) is returned to the caller.
// 'new' triggers the constructor
// Space is reserved for a new Point object
Point myPoint = new Point(5, 10);
Overloaded Constructors
A class can have more than one constructor. This is called overloading. Each constructor must have a different parameter list (different types, number, or order of parameters). This allows you to create objects in different ways.
public class Smartphone {
private String brand;
private int battery;
// Constructor 1: Sets both values
public Smartphone(String b, int bat) {
brand = b;
battery = bat;
}
// Constructor 2: Sets a default battery level
public Smartphone(String b) {
brand = b;
battery = 100; // Default starting battery
}
}
Mutable Objects as Parameters
A mutable object is one whose state can be changed (like an ArrayList or a custom object). When a mutable object is passed to a constructor, it is best practice to initialize the instance variable with a copy of that object.
- If you assign a parameter's reference directly to an instance variable, both the class and the external code are looking at the exact same object. This allows external code to bypass your class's encapsulation and change its internal state unexpectedly.
The Risk of Direct Reference
If you store the original reference instead of making a copy, any changes made to the original object outside the class will also change your object's internal data.
Task: Observing the risk of storing direct object references.
public class Line {
private Point startPoint;
// BAD PRACTICE: Storing the direct reference
public Line(Point p) {
this.startPoint = p;
}
}
// --- In another class ---
Point myPoint = new Point(0, 0);
Line myLine = new Line(myPoint);
// If we change myPoint here...
myPoint.setX(500);
// ...myLine's startPoint ALSO changes to 500!
// This breaks the encapsulation of the Line class.
The Solution: Defensive Copying To prevent this, we create a new object inside the constructor using the data from the parameter.
Task: Implementing defensive copying to ensure class independence.
// GOOD PRACTICE: Making a copy
public Line(Point p) {
// Create a new Point so our Line is independent
this.startPoint = new Point(p.getX(), p.getY());
}
The Default Constructor
If you do not write any constructors for your class, Java automatically provides a no-parameter constructor.
- This is called the default constructor.
- It sets all instance variables to their default values (e.g., 0, null, false).
Important: As soon as you write any constructor (even one with parameters), Java stops providing the default one.
Task: Identifying the automatic default constructor.
public class EmptyClass {
private int x;
// No constructor written, so Java provides one.
// We can still say:
// EmptyClass obj = new EmptyClass();
}
Default Values
When no constructor is provided (or if a constructor doesn't set a variable), Java assigns these defaults:
| Data Type | Default Value |
|---|---|
int |
0 |
double |
0.0 |
boolean |
false |
Reference Types (e.g., String, Engine) |
null |
Task: Reviewing default initialization values for instance variables.
public class Gadget {
private int id; // Will be 0
private boolean active; // Will be false
private String name; // Will be null
}
- In Java, you can use
this()to have one constructor call another constructor (called constructor chaining). However, this is not part of the AP CSA course and you do not need to know it for the exam.