The Java Textbook

Part 4 · data-collections

Arrays

The largest unit on the exam starts here. Fixed length, zero-indexed, and the two off-by-one errors that account for most lost marks.

By the end of this chapter you can

  • Declare, create and initialise an array
  • Traverse an array with both loop forms and know when each is usable
  • Avoid ArrayIndexOutOfBoundsException at both ends
  • Explain why an array parameter can be modified by a method

Unit 4 is 30–40% of the exam score — the largest single unit, and larger than Units 1 and 3 combined. It starts with arrays, and almost everything later in the unit is a loop over one.

Three ways to make one

Declaring and creating
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        // 1. Size only: every element gets a default value.
        int[] a = new int[4];

        // 2. Listed values, length inferred.
        int[] b = {10, 20, 30};

        // 3. Same, written out in full — needed when not on a declaration line.
        int[] c = new int[]{7, 8};

        System.out.println(Arrays.toString(a));   // defaults
        System.out.println(Arrays.toString(b));
        System.out.println(Arrays.toString(c));
        System.out.println("lengths: " + a.length + " " + b.length + " " + c.length);
    }
}

The defaults are worth knowing because the exam tests them: 0 for numeric types, false for boolean, and null for any object type — including String, which is the one that surprises people.

Defaults, including the dangerous one
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] ints = new int[3];
        boolean[] flags = new boolean[3];
        String[] words = new String[3];

        System.out.println(Arrays.toString(ints));
        System.out.println(Arrays.toString(flags));
        System.out.println(Arrays.toString(words));

        // A String[] is full of nulls, not empty strings:
        System.out.println("is it \"\"? " + "".equals(words[0]));
        System.out.println("is it null? " + (words[0] == null));
    }
}

Calling a method on one of those nulls throws NullPointerException, and it is a common way for an FRQ to fail at run time after looking complete.

length is a field, not a method

array.length      // arrays — no parentheses
string.length()   // Strings — parentheses
list.size()       // ArrayList — different name entirely

Three collections, three spellings, and mixing them up is a compile error rather than a silent bug — which makes it annoying rather than dangerous. It still costs time on a written FRQ, where the compiler is not there to help.

Traversing

Both loop forms
public class Main {
    public static void main(String[] args) {
        int[] scores = {88, 95, 72, 100};

        // Indexed: you know where you are, and you can write.
        int total = 0;
        for (int i = 0; i < scores.length; i++) {
            total += scores[i];
        }

        // Enhanced: shorter, read-only over the array itself.
        int best = scores[0];
        for (int s : scores) {
            if (s > best) best = s;
        }

        System.out.println("total " + total + ", best " + best);
    }
}

Choose the indexed form when you need the position — to compare neighbours, to write into the array, or to start somewhere other than the beginning. Choose the enhanced form when you only need the values.

The two off-by-one errors

Valid indices run from 0 to length - 1. Both ends have a classic mistake.

The last element is length - 1
public class Main {
    public static void main(String[] args) {
        int[] a = {1, 2, 3};
        System.out.println("last is " + a[a.length - 1]);
        System.out.println("about to go one too far…");
        System.out.println(a[a.length]);     // throws
    }
}

That sample is asserted to throw, and the verifier checks that it does — a demonstration of a crash that stopped crashing would be worse than useless.

The loop condition is the place this is decided:

  • i < a.length — correct.
  • i <= a.length — one too far, and throws on the last iteration.

Arrays are objects

An array variable holds a reference. Two consequences that the exam tests directly:

Passing an array lets a method change it
import java.util.Arrays;

public class Main {
    static void zeroFirst(int[] data) {
        data[0] = 0;               // reaches through the reference
    }

    static void reassign(int[] data) {
        data = new int[]{99, 99};  // rebinds the local copy only
    }

    public static void main(String[] args) {
        int[] nums = {5, 6, 7};

        zeroFirst(nums);
        System.out.println("after zeroFirst: " + Arrays.toString(nums));

        reassign(nums);
        System.out.println("after reassign:  " + Arrays.toString(nums));
    }
}

The first method changes the caller’s array; the second does not. Java passes the reference by value — the method gets its own copy of the arrow, so it can follow the arrow and alter what is there, but pointing its copy somewhere else has no effect outside.

That single distinction explains every array-parameter question on the exam.

Practice