What Are Objects?
From parallel arrays to bundled data — the OOP mental shift
After this lesson, you will be able to:
- Explain why parallel arrays are fragile and what problem objects solve
- Distinguish between a class (blueprint) and an object (instance)
- Create objects using the
newkeyword and call methods on them - Draw a memory diagram showing references on the stack and objects on the heap
- Explain what
nullmeans and whyNullPointerExceptionoccurs - Identify objects you have already used:
Scanner,String,File,ArrayList
Three Parallel Arrays and a Bug
You have 100 students. Each has a name, a GPA, and an ID. With the tools you have so far, you would write this:
String[] names = new String[100];
double[] gpas = new double[100];
int[] ids = new int[100];
names[0] = "Alice";
gpas[0] = 3.8;
ids[0] = 1001;
names[1] = "Bob";
gpas[1] = 3.2;
ids[1] = 1002;
It works. Index 0 is Alice across all three arrays. Index 1 is Bob. But the connection between names[0], gpas[0], and ids[0] exists only in your head. Java does not enforce it. And that is where things break.
Sort names alphabetically. The GPAs and IDs do not follow. Now names[0] is “Alice” but gpas[0] still holds whatever was there before the sort — maybe Bob’s GPA. Delete a student from one array but forget the other two. No exception. No compiler warning. Just silently corrupted data that produces wrong answers.
Want to pass a student to a method? You pass three separate values:
public static void printStudent(final String name, final double gpa, final int id) {
System.out.println(name + " (ID: " + id + ", GPA: " + gpa + ")");
}
printStudent(names[0], gpas[0], ids[0]);
Add an email field later and every method signature, every call site, and every piece of code that touches students must change. The code does not match the concept. You think “a student” as one thing, but the code stores it as three disconnected arrays held together by hope and careful indexing.
From CSCD 110: In Python, you might have used parallel lists the same way —
names = [],gpas = [],ids = []. Python also lets you use dictionaries or tuples to bundle data, but it has no way to enforce that a “student” always has exactly three fields of specific types. Java classes give you that enforcement at compile time.
The Fix: Bundle Data into Objects
An object is a single unit that packages related data and related behavior together. Instead of three arrays, you define a Student class and create one array of Student objects:
public class Student {
private String name;
private double gpa;
private int id;
public Student(final String name, final double gpa, final int id) {
this.name = name;
this.gpa = gpa;
this.id = id;
}
public String getName() { return this.name; }
public double getGpa() { return this.gpa; }
public int getId() { return this.id; }
}
Now the usage is clean:
Student[] roster = new Student[100];
roster[0] = new Student("Alice", 3.8, 1001);
roster[1] = new Student("Bob", 3.2, 1002);
System.out.println(roster[0].getName()); // "Alice"
Sort the array and each student’s name, GPA, and ID move together — they are part of the same object. Delete one element and the entire student is gone. Pass a student to a method with one parameter instead of three. Add a field later and only the Student class changes; every method that accepts a Student automatically has access to the new data.
Key Insight: Data that belongs together should be stored together. Objects solve the fragmentation problem by bundling fields and methods into a single, self-contained unit.
Classes vs. Objects: Blueprints vs. Instances
These two terms are the foundation of everything this week.
A class is a blueprint. It describes what fields an object will have and what methods it can perform. You write the class once. It defines a new type — just like int and String are types, Student is a type you created.
An object (also called an instance) is a real thing built from that blueprint. Each object has its own copy of the fields with its own values. You can create as many objects from one class as you want, and each is independent.
Think of it this way: an architect draws blueprints for a house. The blueprint describes rooms, plumbing, electrical. But you cannot live in a blueprint. When a crew builds a house from those blueprints, that house is an instance. They can build 50 houses from the same blueprint, each with its own paint color and furniture.
// One class (blueprint):
public class Student { ... }
// Three objects (instances), each with their own data:
Student alice = new Student("Alice", 3.8, 1001);
Student bob = new Student("Bob", 3.2, 1002);
Student charlie = new Student("Charlie", 3.5, 1003);
alice, bob, and charlie are three separate objects. They all came from the same Student blueprint, so they all have a name, a gpa, and an id. But Alice’s name is “Alice” and Bob’s name is “Bob” — each object holds its own values.
How many Student objects exist after this code runs?
Student a = new Student("A", 3.0, 1);
Student b = new Student("B", 3.5, 2);
Student c = a;
The new Keyword and Object References
When you declare a primitive variable, the variable holds the value directly:
int age = 25; // age IS the number 25
Object variables work differently. The variable does not contain the object itself — it contains a reference, an arrow that points to the object somewhere else in memory:
Student alice = new Student("Alice", 3.8, 1001);
// alice is NOT the Student -- alice is an arrow pointing TO the Student
The new keyword does four things:
- Allocates memory on the heap for a new
Studentobject - Calls the constructor to initialize the fields
- Returns a reference (an arrow) to the new object
- Stores that reference in the variable
alice
Here is what memory looks like after creating two students:
Stack Heap
┌─────────────┐ ┌─────────────────────┐
│ alice: ──────┼─────────────►│ name: "Alice" │
│ │ │ gpa: 3.8 │
│ │ │ id: 1001 │
├─────────────┤ └─────────────────────┘
│ bob: ──────┼──────┐
│ │ │ ┌─────────────────────┐
└─────────────┘ └─────►│ name: "Bob" │
│ gpa: 3.2 │
│ id: 1002 │
└─────────────────────┘
The stack (left side) holds the reference variables. The heap (right side) holds the actual objects. Each variable on the stack is an arrow pointing to an object on the heap. This is the same model you saw with arrays in Lesson 2.1 — arrays are also reference types that live on the heap.
What Happens with Assignment
When you assign one object variable to another, you copy the reference, not the object:
Student alice = new Student("Alice", 3.8, 1001);
Student copy = alice; // copy points to the SAME object
Stack Heap
┌─────────────┐ ┌─────────────────────┐
│ alice: ──────┼─────────────►│ name: "Alice" │
├─────────────┤ ┌───►│ gpa: 3.8 │
│ copy: ──────┼─────────┘ │ id: 1001 │
└─────────────┘ └─────────────────────┘
Both alice and copy point to the same object. Change the GPA through copy and alice sees it too. This is the same aliasing behavior you learned with arrays.
What is the key difference between int x = 5; and Student s = new Student("A", 3.0, 1);?
The null Reference
Sometimes a reference variable does not point to any object. Java provides the special value null:
Student student = null; // the arrow points to nothing
System.out.println(student); // prints "null"
null means “no object here.” You can later assign a real object:
student = new Student("Alice", 3.8, 1001);
But if you try to call a method on null, Java throws a NullPointerException:
Student student = null;
System.out.println(student.getGpa()); // NullPointerException!
Java tries to follow the arrow to call getGpa(), but the arrow points nowhere. There is no object to operate on.
The Trick: When you see a
NullPointerException, ask: “Why is this variablenullwhen I expected it to have an object?” Usually you forgot to initialize it withnew. Ifnullis a legitimate possibility (a search that found nothing, for example), guard against it:if (student != null) { System.out.println(student.getGpa()); }
null in Arrays of Objects
When you create an array of objects, every slot starts as null:
Student[] roster = new Student[3];
// roster is [null, null, null] — no Student objects exist yet!
Stack Heap
┌─────────────┐ ┌──────┬──────┬──────┐
│ roster: ─────┼─────────────►│ null │ null │ null │
└─────────────┘ └──────┴──────┴──────┘
You must fill each slot with new:
roster[0] = new Student("Alice", 3.8, 1001);
roster[1] = new Student("Bob", 3.2, 1002);
roster[2] = new Student("Charlie", 3.5, 1003);
Common Pitfall: Creating an array of objects does NOT create the objects themselves.
new Student[3]creates three reference slots, all set tonull. You must callnew Student(...)for each slot. Forgetting this is the number one source ofNullPointerExceptionwhen working with object arrays.
Objects You Have Already Used
You have been working with objects since the first week of this course. Every time you used a String, a Scanner, a File, an ArrayList, or a Random, you were using an object.
| Class (Blueprint) | Data It Holds | Methods You Have Called |
|---|---|---|
String |
Text characters | .length(), .toUpperCase(), .substring() |
Scanner |
Input stream + position | .nextLine(), .nextInt(), .hasNextInt() |
File |
File path | .exists() |
ArrayList |
Dynamic list of items | .add(), .get(), .remove(), .size() |
Random |
RNG state | .nextInt(), .nextDouble() |
When you wrote new Scanner(System.in), you told Java: “Create a new object from the Scanner blueprint and give it System.in as the input stream to read from.” You have been a consumer of classes all quarter. Now you are going to learn to design your own.
Complete Example: From Parallel Arrays to Objects
Here is the full transformation. First, the parallel-array version:
import java.util.Scanner;
public class ParallelArrayDemo {
public static void main(final String[] args) {
String[] names = {"Alice", "Bob", "Charlie"};
double[] gpas = {3.8, 3.2, 3.5};
int[] ids = {1001, 1002, 1003};
// Print all students
for (int i = 0; i < names.length; i++) {
System.out.println(names[i] + " (ID: " + ids[i]
+ ", GPA: " + gpas[i] + ")");
}
// Find the student with the highest GPA
int bestIndex = 0;
for (int i = 1; i < gpas.length; i++) {
if (gpas[i] > gpas[bestIndex]) {
bestIndex = i;
}
}
System.out.println("Highest GPA: " + names[bestIndex]
+ " with " + gpas[bestIndex]);
}
}
Now the object version using the Student class from earlier:
public class ObjectDemo {
public static void main(final String[] args) {
Student[] roster = new Student[3];
roster[0] = new Student("Alice", 3.8, 1001);
roster[1] = new Student("Bob", 3.2, 1002);
roster[2] = new Student("Charlie", 3.5, 1003);
// Print all students
for (int i = 0; i < roster.length; i++) {
System.out.println(roster[i].getName() + " (ID: "
+ roster[i].getId() + ", GPA: "
+ roster[i].getGpa() + ")");
}
// Find the student with the highest GPA
int bestIndex = 0;
for (int i = 1; i < roster.length; i++) {
if (roster[i].getGpa() > roster[bestIndex].getGpa()) {
bestIndex = i;
}
}
System.out.println("Highest GPA: " + roster[bestIndex].getName()
+ " with " + roster[bestIndex].getGpa());
}
}
The logic is the same. But the object version keeps each student’s data bundled together. Sort the array and every field travels with its student. Add an email field and ObjectDemo does not need to change at all — only Student does.
What happens when you run this code?
Student[] roster = new Student[5];
System.out.println(roster[0].getName());
Why This Matters for CSCD 211
Objects are not just a topic for one week. They are the organizing principle of nearly every Java program you will write from now on. In CSCD 211, you will learn inheritance (one class building on another), polymorphism (treating different types through a common interface), and design patterns (proven recipes for structuring classes). All of that depends on understanding what objects are, how references work, and why bundling data with behavior matters.
For the rest of this quarter, every new concept — toString(), equals(), Comparable, interfaces — builds on the foundation you learned today. The mental model of references on the stack pointing to objects on the heap will explain nearly every surprising behavior you encounter.
Quick Reference
| Concept | Key Point |
|---|---|
| Class | A blueprint that defines fields and methods. Written once. |
| Object (instance) | A real thing built from a class, with its own field values. Created with new. |
| Reference | An arrow stored in a variable that points to an object on the heap. |
new |
Allocates an object on the heap, calls the constructor, returns a reference. |
null |
A reference that points to no object. Calling a method on null throws NullPointerException. |
| Parallel arrays | Fragile pattern where related data is split across separate arrays. Objects replace this. |
Summary
Parallel arrays store related data in separate arrays linked only by index. They break when you sort, delete, or add fields. Objects solve this by bundling related data (fields) and behavior (methods) into a single unit.
A class is a blueprint; an object is an instance built from that blueprint with new. Object variables hold references (arrows) to objects on the heap, not the objects themselves. Assigning one object variable to another copies the reference, creating an alias. The special value null means “no object” — calling a method on null throws NullPointerException.
You have been using objects all quarter: Scanner, String, File, ArrayList. Now you will learn to write your own classes from scratch.
Next lesson: Writing Your First Class — fields, constructors, methods, and the this keyword.