7.1 Defining Classes and Instantiating Objects
Key Takeaways
- A class serves as a compile-time blueprint defining state (fields) and behavior (methods), whereas an object is a dynamic runtime instance allocated on the Java heap.
- The new operator allocates memory on the Java heap, initializes instance variables to their language default values, executes the constructor, and returns the heap memory address.
- Stack memory stores method call frames and reference variables holding memory addresses, while heap memory stores the actual object instances and their instance variables.
- Assigning one reference variable to another copies the reference address (aliasing), meaning both variables point to the identical object on the heap.
- An object becomes eligible for Garbage Collection (GC) the moment it can no longer be reached from any active GC root, including groups of objects in an island of isolation.
7.1 Defining Classes and Instantiating Objects
[!NOTE] Exam Focus: Oracle's "Classes and Constructors" topic area carries seven objectives — more than any other topic on the 1Z0-811 outline — starting with "create a new class including a main method" and "describe the relationship between an object and its members". Candidates must master the architectural distinction between class blueprints and runtime objects, the physical mechanics of stack-based reference variables versus heap-allocated instances, member access via the dot operator, reference aliasing, default field initialization, and the exact lifecycle stages that render an object eligible for automatic Garbage Collection (GC).
Java is fundamentally an Object-Oriented Programming (OOP) language. While procedural programming languages (such as C) organize software around sequential procedures and standalone functions that process detached data, the object-oriented paradigm models software as a collection of cooperating objects. Each object encapsulates both its internal state (the data attributes it stores) and its public behavior (the operations and algorithms it can execute).
1. Classes vs. Objects: The Blueprint and the Instance
The relationship between a class and an object is one of the most fundamental concepts tested on the 1Z0-811 examination:
- Class (The Blueprint): A class is a programmer-defined blueprint, template, or prototype from which individual objects are created. Written in
.javasource files and compiled into.classbytecode files, a class defines the structure of data (fields) and operations (methods). A class exists as static metadata loaded into the JVM's Metaspace memory area. By itself, a class allocates no dynamic heap memory for application data. - Object (The Instance): An object is a concrete, physical instance of a class created at runtime. When an object is instantiated, the Java Virtual Machine (JVM) dynamically allocates a dedicated block of memory on the Java Heap to hold that specific object's data.
The Real-World Architectural Analogy
Consider an architectural blueprint for a modern residence. The blueprint specifies dimensions, room counts, and electrical circuitry, but you cannot live inside a blueprint. To inhabit the home, builders must use the blueprint to construct an actual physical house. From a single blueprint, builders can construct dozens of distinct physical houses. Each house occupies its own physical plot of land, can be painted a different color, and can be modified independently of the other houses. In Java, the blueprint is the class, while each constructed house is an object.
Declaring a Java Class
A minimal Java class definition begins with the class keyword followed by the class identifier:
public class Car {
// Class body enclosed in curly braces
}
Standard Java naming conventions dictate that class identifiers use UpperCamelCase (also known as PascalCase), beginning with an uppercase letter (BankAccount, OrderProcessor). Under standard Java compilation rules, if a class is declared public, the source file name must match the class name identically, including case (Car.java).
2. Anatomy of a Class: State (Fields) and Behavior (Methods)
A Java class bundles two essential software components together:
public class Car {
// 1. STATE: Instance Variables (Fields / Attributes)
String make;
String model;
int year;
double mileage;
boolean isRunning;
// 2. BEHAVIOR: Instance Methods
public void startEngine() {
isRunning = true;
System.out.println("Engine started.");
}
public void drive(double distance) {
if (isRunning) {
mileage += distance;
System.out.println("Drove " + distance + " miles. Total: " + mileage);
} else {
System.out.println("Cannot drive: start engine first!");
}
}
}
State: Instance Variables
Instance variables (often called fields or attributes) represent the data held by an individual object instance. Crucially, every instantiated object possesses its own independent copy of instance variables. If your program instantiates three distinct Car objects, three separate mileage variables exist across heap memory.
Default Initialization of Instance Variables
Unlike local variables declared inside method bodies (which contain no default values and must be explicitly initialized before being read), instance variables are automatically initialized to language default values upon heap allocation:
| Data Type Category | Specific Java Type | Default Value |
|---|---|---|
| Integer Primitives | byte, short, int, long | 0 (or 0L for long) |
| Floating-Point Primitives | float, double | 0.0f / 0.0d (0.0) |
| Character Primitive | char | '\u0000' (null character, integer 0) |
| Boolean Primitive | boolean | false |
| Reference Types | Any Class, Interface, Array (String, Car, int[]) | null |
Instance Variables vs. Local Variables
The 1Z0-811 examination frequently tests candidate awareness of the differences between instance fields and local variables:
- Instance Variables: Declared inside the class body but outside any method, constructor, or block. Allocated on the heap as part of the object. Born when the object is instantiated via
new; destroyed when the object is garbage collected. Automatically receive default values. - Local Variables: Declared inside a method, constructor, or block (including method parameters). Allocated on the thread stack. Born when the enclosing block is entered; destroyed when the block exits. Never receive default values. Attempting to read an uninitialized local variable results in a fatal compile-time error (
variable x might not have been initialized).
Behavior: Instance Methods
Instance methods define the operations that can be performed on or by an object. Instance methods have direct access to the object's instance fields, allowing them to inspect, compute, and mutate the object's internal state over time.
3. Instantiating Objects with the new Operator
Creating an object in Java is known as instantiation. Instantiation involves three distinct syntactic and operational steps:
Car myCar = new Car();
\_______/ \_______/ \_______/ \_______/
Type 1. Declaration 2. Instantiation 3. Initialization
(reference (stack slot for (allocates heap (constructor runs
type) the pointer) memory) on the new object)
- Declaration:
Car myCar;declares a reference variable namedmyCarof typeCar. This statement allocates a variable slot on the JVM Thread Stack, but creates no object on the heap. At this stage,myCarholds the valuenull(or is uninitialized if declared as a local variable). - Instantiation: The
newkeyword is an operator that commands the JVM to dynamically allocate a new block of physical memory on the Java Heap. The JVM calculates the exact byte size required for all instance variables declared byCar(and its superclasses) and zero-initializes that memory block. - Initialization: The constructor invocation
Car()immediately follows thenewoperator. The constructor executes to initialize the newly allocated object's state. - Assignment: The
newexpression evaluates to the memory reference address of the freshly allocated heap object. The assignment operator (=) stores that address into the stack reference variablemyCar.
The Dot Operator (.)
Once a reference variable points to a valid object on the heap, you interact with the object using the member access operator, commonly known as the dot operator (.):
Car myCar = new Car();
// Accessing fields (reading and writing)
myCar.make = "Toyota";
myCar.year = 2024;
System.out.println("Vehicle Year: " + myCar.year);
// Invoking instance methods
myCar.startEngine();
myCar.drive(45.5);
If you attempt to apply the dot operator to a reference variable that holds null (e.g., Car emptyCar = null; emptyCar.startEngine();), the JVM throws a runtime NullPointerException.
4. Stack vs. Heap: Reference Variables vs. Heap Objects
Understanding the runtime memory separation between the JVM Stack and the Java Heap is critical for 1Z0-811 candidates:
| Attribute | JVM Stack Memory | Java Heap Memory |
|---|---|---|
| Primary Role | Stores active thread call frames, local primitives, and reference variables | Stores all instantiated objects and array instances |
| Lifecycle | Automatic; allocated when a method is entered, popped when the method returns | Managed dynamically; persists as long as reachable from a live GC root |
| Storage Content | Primitive values (e.g., int x = 5;) or memory addresses (e.g., 0x7A4F) | Actual object instance variables and object metadata headers |
| Thread Access | Private to each individual executing thread | Shared globally across all threads in the JVM process |
| Allocation Speed | Extremely fast pointer adjustments | Dynamic memory allocation managed by the JVM allocator |
| Default Values | None for local variables (compiler requires assignment) | Automatic default values for all instance fields |
public void createVehicle() {
int wheelCount = 4; // Primitive stored directly on the Stack
Car familyCar = new Car(); // 'familyCar' reference on Stack; Car object on Heap
}
[!IMPORTANT] Pointers, Not Objects: A reference variable never contains the actual object data itself. A reference variable is merely a pointer—a value holding the memory address where the object resides on the heap. When you access an object property (such as
familyCar.mileage), the JVM uses the address stored infamilyCarto traverse into heap memory and read themileagefield.
5. Multiple Reference Variables Pointing to the Same Object (Aliasing)
In Java, assigning one reference variable to another does not copy the object. Instead, it copies the reference address stored inside the variable. This creates aliasing—multiple reference variables on the stack pointing to the exact same object on the heap:
Car carA = new Car();
carA.mileage = 150.0;
// Copy the reference address from carA into carB
Car carB = carA;
// Mutate the object via carB
carB.mileage = 300.0;
// Inspect the object via carA
System.out.println(carA.mileage); // Prints: 300.0!
System.out.println(carA == carB); // Prints: true (both hold identical heap addresses)
Because carA and carB hold the exact same memory address, any modification made through carB is immediately visible when inspecting carA. Furthermore, the equality operator (==) compares the primitive addresses held inside reference variables; because both variables point to the same heap address, carA == carB evaluates to true.
6. Object Lifecycle and Garbage Collection Eligibility
Java eliminates manual memory deallocation. Unlike languages where developers must pair every allocation with explicit deallocation commands, Java features an automated Garbage Collector (GC). The Garbage Collector is a background daemon thread managed by the JVM that automatically detects and reclaims heap memory occupied by objects that are no longer in use.
The Core Rule of GC Eligibility
An object on the heap becomes eligible for garbage collection at the precise moment it is no longer reachable by any live thread through any chain of valid reference pointers starting from a GC Root.
A GC Root is any reference that is intrinsically reachable, including:
- Local reference variables currently active in any thread's call stack frame.
- Static reference variables belonging to loaded classes in Metaspace.
- Active Java Native Interface (JNI) references.
The Three Classic Dereferencing Scenarios
The 1Z0-811 exam frequently provides code snippets and asks candidates to identify the exact line where an object becomes eligible for GC. Objects lose reachability via three primary mechanisms:
1. Reference Explicitly Assigned to null
Car c = new Car(); // Car Object 1 allocated on Heap; c points to Object 1
c = null; // c no longer points to Object 1. Object 1 is now eligible for GC!
2. Reference Variable Reassigned to Another Object
Car c1 = new Car(); // Car Object 1 allocated
Car c2 = new Car(); // Car Object 2 allocated
c1 = c2; // c1 now points to Object 2!
// Car Object 1 has ZERO references pointing to it.
// Car Object 1 is now eligible for GC!
3. Reference Variable Goes Out of Scope
When a method or block finishes executing, its stack frame is popped from the JVM stack. All local reference variables declared within that block cease to exist:
public void executeTask() {
Car tempCar = new Car(); // tempCar points to Object 1 on Heap
tempCar.drive(10);
} // Method terminates! The stack frame pops, destroying 'tempCar'.
// Object 1 has no remaining references and is eligible for GC immediately!
[!WARNING] The
System.gc()Fallacy: CallingSystem.gc()orRuntime.getRuntime().gc()merely sends a polite suggestion or hint to the JVM that garbage collection may be beneficial. The JVM specification provides no guarantee that the garbage collector will execute immediately, execute at all, or collect any specific object upon invokingSystem.gc(). Never rely onSystem.gc()for predictable program behavior or exam answers!
7. Islands of Isolation: Circular References
A classic, high-yield exam topic involves islands of isolation. Consider two objects that contain fields referencing each other:
class Node {
Node neighbor;
}
public class IslandDemo {
public static void main(String[] args) {
Node n1 = new Node(); // Object A created
Node n2 = new Node(); // Object B created
n1.neighbor = n2; // Object A points to Object B
n2.neighbor = n1; // Object B points to Object A
// Cut off external references from the Stack
n1 = null;
n2 = null;
// POINT X: Are Object A and Object B eligible for GC?
}
}
At Point X, Object A still holds a reference to Object B (via its neighbor field), and Object B still holds a reference to Object A. However, neither Object A nor Object B can be reached from any active GC root on the thread stack!
Because Java uses tracing garbage collection (reachability analysis) rather than naive reference counting, the Garbage Collector starts at active stack frames and static fields. Finding no path connecting any live thread to the mutually referencing group, the JVM recognizes that Object A and Object B constitute an isolated island. Both objects become eligible for garbage collection simultaneously at Point X.
Consider the following code snippet:
At Point X, how many Device objects created in main are eligible for garbage collection?public class DeviceManager {
public static void main(String[] args) {
Device d1 = new Device("Laptop");
Device d2 = new Device("Tablet");
Device d3 = d1;
d1 = null;
d2 = d3;
// Point X
}
}
Consider the following class definition and test execution:
What is the output of running this program?class Account {
int balance;
}
public class BankTest {
public static void main(String[] args) {
Account a1 = new Account();
a1.balance = 500;
Account a2 = a1;
a2.balance = 800;
System.out.println(a1.balance + " " + (a1 == a2));
}
}
Consider two objects, Node A and Node B. Node A has an instance field pointing to Node B, and Node B has an instance field pointing to Node A. All external stack reference variables pointing to Node A and Node B are subsequently assigned to null. Which statement correctly describes the Garbage Collection status of Node A and Node B?