Once your programs start handling real data, separate variables stop scaling. A class of three students fits neatly into s1, s2, s3; a class of three hundred does not. You need a single container that holds many values of the same type and lets you reach any one of them by position.
That container is an array. An array is a fixed-length, indexed sequence of values of one type — ten ints, four Strings, a hundred doubles. The length is chosen when the array is created and never changes afterwards; you cannot push a new score onto the end the way you can in Python or JavaScript. What you get in return is speed and simplicity: every element sits in one contiguous block, and reaching element i is a single, predictable calculation.
This lesson covers how to build an array, read and change its elements, and walk through all of them with a loop.
Creating and accessing an array
The shortest way to make an array is a literal — the values listed inside curly braces:
int[] scores = {12, 25, 8, 40};
int[] is the type (an array of int), and scores is the name. Elements are numbered from zero, so the first score is scores[0] and the last is scores[3] — never scores[4]. The expression inside the brackets is the index, and it can be any int expression, including a loop variable.
Every array carries its own length as scores.length. Notice it is a field, not a method: no parentheses. For a four-element array, length is 4, while the highest valid index is 3. That gap of exactly one is the root of the most common array bug.
public class Main {
public static void main(String[] args) {
int[] scores = {12, 25, 8, 40};
System.out.println("first: " + scores[0]);
System.out.println("count: " + scores.length);
System.out.println("last: " + scores[scores.length - 1]);
}
}
flowchart LR A0["index 0<br/>value 12"] --> A1["index 1<br/>value 25"] --> A2["index 2<br/>value 8"] --> A3["index 3<br/>value 40"] LEN["length is 4<br/>last index is 3"] -.-> A3 style A0 fill:#3776ab,color:#fff style A3 fill:#b45309,color:#fff
The bounds trap
Access an index that does not exist and Java throws ArrayIndexOutOfBoundsException — immediately, with no chance to recover unless you catch it. The trap is the off-by-one: a loop that runs i <= scores.length instead of i < scores.length processes one index too many, because scores.length is one past the last valid slot.
for (int i = 0; i < scores.length; i++) {
System.out.println(scores[i]);
}
The condition i < scores.length stops i at exactly the right place: it visits 0, 1, 2, 3 for a four-element array and never asks for scores[4]. Build this reflex now, because the same < versus <= decision haunts every indexed loop you will ever write.
Two ways to traverse: indexed and for-each
Java gives you two looping forms over an array. The indexed loop gives you the position as well as the value, which you need whenever you plan to change an element or compare neighbours. The enhanced for, often called for-each, hides the index entirely and just hands you each value in turn — ideal when you only want to read.
for (int s : scores) { // for-each: read each value
System.out.println(s);
}
Read the colon as in: for each int s in scores. You never touch an index, so you can never be off by one — and that safety is exactly the feature that sets up the next trap.
flowchart TD
subgraph IDX["indexed for loop"]
I1["i = 0"] --> I2{"i < length?"}
I2 -->|"yes"| I3["use the element"]
I3 --> I4["i++"]
I4 --> I2
I2 -->|"no"| I5["done"]
end
subgraph FE["enhanced for-each"]
F1["take next element"] --> F2["x = that value"]
F2 --> F3["use x"]
F3 --> F1
end
style IDX fill:#3776ab,color:#fff
style FE fill:#166534,color:#fff
public class Main {
public static void main(String[] args) {
int[] prices = {3, 7, 2, 8};
int sum = 0;
for (int i = 0; i < prices.length; i++) {
sum = sum + prices[i];
}
System.out.println("total: " + sum);
}
}
flowchart LR ARR["array element<br/>value 5"] -->|"copied into"| X["loop variable x = 5"] X -->|"multiply x by 10"| X2["x = 50<br/>but element still 5"] X2 -.->|"no write back"| ARR style X2 fill:#b45309,color:#fff
Changing an element
Because each slot has an index, updating a value is just an assignment with brackets: scores[2] = 99; replaces whatever lived at index 2 and leaves the other slots alone. The array stays the same object, the same length — only one cell changed. You can read that cell back immediately on the next line.
This is what makes arrays so predictable to reason about: a write touches exactly one position, and every other element is guaranteed unaffected. Contrast that with a single integer variable, where reusing the name for a new value erases the old one entirely. An array keeps every value alive and addressable, which is the whole reason to reach for one.
Default values and the fixed length
When you create an array with new int[5] instead of a literal, Java fills it with the default value for its type: 0 for whole numbers, false for booleans, null for objects. The slots exist immediately; they are simply zeroed until you write into them. A literal like {12, 25} sets the values and fixes the length in one stroke.
Arrays are mutable — you can overwrite any element through its index at any time — but their length is fixed. You cannot grow a four-slot array into a fifth slot; if you need that, Java's collections, which you will meet later, are the right tool. For a known, stable set of values, an array is exactly enough.
Array basics. Predict the exact output (three lines) — remember indices start at 0.
public class Main {
public static void main(String[] args) {
int[] scores = {12, 25, 8, 40};
System.out.println(scores[0]);
System.out.println(scores.length);
System.out.println(scores[3]);
}
}
scores[0] is the first element, which is 12.
scores.length is the number of slots, which is 4.
scores[3] is the last element, which is 40 (index 3 is the fourth slot).
Indexed traversal. Predict the exact output (two lines) — follow the sum and recall the first element.
public class Main {
public static void main(String[] args) {
int[] prices = {3, 7, 2, 8};
int sum = 0;
for (int i = 0; i < prices.length; i++) {
sum = sum + prices[i];
}
System.out.println(sum);
System.out.println(prices[0]);
}
}
The loop visits every index because it stops when i reaches prices.length.
sum becomes 3 + 7 + 2 + 8 = 20.
prices[0] is 3, and the loop never modified the array, so it is still 3.
An array was created with int[] a = {5, 10, 15, 20};. Which index holds the value 20, and what is a.length?
Indices start at 0, so a four-element array uses indices 0, 1, 2, 3. The last element (20) lives at index 3, and a.length is 4. Asking for index 4 would reach one slot past the end and throw ArrayIndexOutOfBoundsException.
The for-each trap. The loop tries to multiply every element by 10. Predict the exact output (three lines) — has the array actually changed?
public class Main {
public static void main(String[] args) {
int[] nums = {1, 2, 3};
for (int x : nums) {
x = x * 10;
}
System.out.println(nums[0]);
System.out.println(nums[1]);
System.out.println(nums[2]);
}
}
Each element is COPIED into x at the start of every pass.
x = x * 10 changes the copy, not the slot it came from.
Because nothing was ever written back through an index, nums is still {1, 2, 3}.
Write through the index. This loop really does modify the array. Predict the exact output (four lines) — track what each slot becomes.
public class Main {
public static void main(String[] args) {
int[] nums = {2, 4, 6, 8};
for (int i = 0; i < nums.length; i++) {
nums[i] = nums[i] + i;
}
System.out.println(nums[0]);
System.out.println(nums[1]);
System.out.println(nums[2]);
System.out.println(nums[3]);
}
}
Assignment through nums[i] really does change the array, unlike a for-each copy.
i = 0: nums[0] = 2 + 0 = 2. i = 1: nums[1] = 4 + 1 = 5.
i = 2: nums[2] = 6 + 2 = 8. i = 3: nums[3] = 8 + 3 = 11.
Recap
- An array is a fixed-length, zero-indexed container of one type. The first element sits at index
0, the last atlength - 1. arr.lengthis a field, not a method, and equals the number of slots — one more than the highest valid index.- Accessing a missing index throws
ArrayIndexOutOfBoundsException. Loop withi < length, neveri <= length. - The indexed loop gives you the position; for-each gives you only the value and is ideal for reading.
- For-each hands you a copy of each element — reassigning it does not change the array. Write through the index to mutate a slot.
Next you will combine arrays with the loop patterns you already know to search, filter, and summarise real data.