Array of Objects
An array of objects lets you store multiple instances of a class in a single, indexed container. It bridges two of Java’s most fundamental concepts — arrays and classes & objects — and is the starting point for understanding how real-world data collections work.
Why Store Objects in an Array?
Imagine you’re writing a student management system. Without object arrays you’d need separate variables for every student’s name, grade, and ID. With an array of objects, you store each Student instance neatly in one place and loop over all of them in a few lines.
Student[] roster = new Student[30]; // 30 Student slots
That’s the core idea. The array itself holds references to objects, not the objects directly.
Declaring and Creating an Object Array
Creating an array of objects is a two-step process:
- Declare and allocate the array (this creates
nullreferences by default). - Create each object and assign it to a slot.
class Student {
String name;
int grade;
Student(String name, int grade) {
this.name = name;
this.grade = grade;
}
}
public class Main {
public static void main(String[] args) {
// Step 1 — allocate array of 3 Student references
Student[] students = new Student[3];
// Step 2 — create each object and assign
students[0] = new Student("Alice", 90);
students[1] = new Student("Bob", 85);
students[2] = new Student("Carol", 92);
// Access a field on the first object
System.out.println(students[0].name + " scored " + students[0].grade);
}
}
Output:
Alice scored 90
Warning: After
new Student[3], every slot containsnull. Calling any method on an uninitialized slot throws aNullPointerException. Always initialize before use.
Inline Initialization with an Array Initializer
If you know all your objects up front, you can declare and populate in one shot using an array initializer:
Student[] students = {
new Student("Alice", 90),
new Student("Bob", 85),
new Student("Carol", 92)
};
This is identical to the two-step approach — Java still allocates the array and assigns each reference — but the syntax is more concise.
Iterating Over an Object Array
Using a Traditional for Loop
for (int i = 0; i < students.length; i++) {
System.out.println(i + ": " + students[i].name + " — " + students[i].grade);
}
Output:
0: Alice — 90
1: Bob — 85
2: Carol — 92
Using the Enhanced for-each Loop
The for-each loop is cleaner when you don’t need the index:
for (Student s : students) {
System.out.println(s.name + " scored " + s.grade);
}
Output:
Alice scored 90
Bob scored 85
Carol scored 92
Tip: Prefer the for-each loop for read-only iteration. Use the indexed
forloop when you need the position or want to modify elements.
Adding Methods to Your Class
Object arrays become far more useful when your class encapsulates behavior. Add a toString() method so printing is easy:
class Student {
String name;
int grade;
Student(String name, int grade) {
this.name = name;
this.grade = grade;
}
@Override
public String toString() {
return name + " (grade: " + grade + ")";
}
boolean isPassing() {
return grade >= 60;
}
}
public class Main {
public static void main(String[] args) {
Student[] students = {
new Student("Alice", 90),
new Student("Bob", 55),
new Student("Carol", 72)
};
for (Student s : students) {
String status = s.isPassing() ? "PASS" : "FAIL";
System.out.println(s + " -> " + status);
}
}
}
Output:
Alice (grade: 90) -> PASS
Bob (grade: 55) -> FAIL
Carol (grade: 72) -> PASS
Note: Overriding toString() is a best practice for any class whose instances you’ll print or log. Java calls
toString()automatically when you pass an object toSystem.out.println.
Searching an Object Array
A simple linear search compares a field value across all elements:
public class Main {
static Student findByName(Student[] arr, String name) {
for (Student s : arr) {
if (s.name.equals(name)) {
return s;
}
}
return null; // not found
}
public static void main(String[] args) {
Student[] students = {
new Student("Alice", 90),
new Student("Bob", 85),
new Student("Carol", 92)
};
Student found = findByName(students, "Bob");
if (found != null) {
System.out.println("Found: " + found);
}
}
}
Output:
Found: Bob (grade: 85)
Tip: Use
String.equals()— not==— when comparing string fields.==checks reference identity, not character content. See String Comparison for details.
Sorting an Object Array
You can sort an object array with Arrays.sort() once your class implements Comparable, or by passing a Comparator:
import java.util.Arrays;
import java.util.Comparator;
public class Main {
public static void main(String[] args) {
Student[] students = {
new Student("Carol", 92),
new Student("Alice", 90),
new Student("Bob", 85)
};
// Sort by grade descending
Arrays.sort(students, Comparator.comparingInt((Student s) -> s.grade).reversed());
for (Student s : students) {
System.out.println(s);
}
}
}
Output:
Carol (grade: 92)
Alice (grade: 90)
Bob (grade: 85)
See Arrays Utility Class for more on Arrays.sort() and other helpers.
Under the Hood
Understanding what Java does in memory helps you avoid subtle bugs.
The Heap Split
When you write Student[] students = new Student[3], Java allocates one array object on the heap. This array stores three references (each 4 or 8 bytes depending on JVM pointer compression), all initialized to null. No Student objects exist yet.
When you write students[0] = new Student("Alice", 90), a separate Student object is created elsewhere on the heap, and its address is stored in students[0].
Stack: Heap (array): Heap (objects):
students -----> [ ref0 | ref1 | ref2 ]
| | |
v v v
Student Student Student
"Alice" "Bob" "Carol"
This indirection has two important consequences:
- Assignment copies the reference, not the object.
students[1] = students[0]makes both slots point to the same Student. Changingstudents[1].gradealso changesstudents[0].grade. - Garbage collection only reclaims a
Studentobject when no references remain. Setting a slot tonull(students[0] = null) removes that reference; the object is eligible for GC if nothing else holds it.
Type Safety via the JVM
The JVM knows the element type of every array at runtime (this is called a reifiable type). If you try to store the wrong object type, you get an ArrayStoreException at runtime, not just a compile error. This is different from generics, which are erased at compile time.
Object[] arr = new Student[3];
arr[0] = new Student("Alice", 90); // OK
arr[1] = "oops"; // ArrayStoreException at runtime!
Performance Notes
- Random access (
students[i]) is O(1) — the JVM calculates the slot address asbase + i * referenceSize. - Iteration is cache-friendly for the array itself (the references are contiguous), but the actual objects can be scattered across the heap, which can reduce CPU cache efficiency for very large arrays.
- For mutable, resizable collections of objects, prefer
ArrayList, which wraps an object array internally and grows it automatically.
Common Pitfalls
| Mistake | What Goes Wrong | Fix |
|---|---|---|
| Forgetting to initialize each slot | NullPointerException | Use a loop or inline initializer to create every object |
Using == to compare field strings | Compares addresses, not content | Use .equals() |
| Copying a slot without cloning | Both references point to one object | Implement a copy constructor or use Object Cloning |
| Accessing out-of-bounds index | ArrayIndexOutOfBoundsException | Always check index against array.length |
Related Topics
- Arrays — foundational array concepts every Java programmer needs
- Classes & Objects — how to define the class you’ll store in your array
- Constructors — how object initialization inside the array works
- ArrayList — the flexible, resizable alternative to a fixed object array
- Comparable — make your objects naturally sortable inside an array
- Arrays Utility Class — sort, search, and fill object arrays with one-liners