Room 1 of 14 · about 30 minutes

Arrays as objects, and where their indices start

This is the first room of the unit, so it assumes nothing.

Tasks checked0 of 4 XP earned on this path0

What this room checks you can do

Student can state that a Java array is a single object holding a fixed-size sequence of same-typed values, and explain what each adjective rules out.

Notes

Fixed-size homogeneous sequence

An array is a single object that holds a fixed-size sequence of values of the same type. Two adjectives carry the weight: fixed-size means once you have constructed the array you cannot grow or shrink it (to "grow" you build a bigger one and copy), and homogeneous means every element has the same declared element type: int[] holds only int values, String[] holds only String references. The array itself is one object on the heap; the variable that names it holds a reference to that object.

This single-object property is why an array can be passed to a method, returned from a method, stored as a field, and aliased by another variable. The slots inside it are not separate variables: they are positions inside one object. That is the mental shift from loops, where every counter or accumulator was its own named variable.

final int[] temps = new int[5];   // one int[] object holding 5 int slots
temps[0] = 72;                    // fill the first slot
temps[1] = 68;                    // ... and so on

In other languages

  • Python: list is the closest analog but is dynamic (it can grow); the truly fixed-size typed counterpart is array.array('i', [0]*5) or numpy.zeros(5, dtype=int).
  • C: int temps[5]; is a fixed-size sequence on the stack; there is no "array object": just a contiguous run of bytes named by the identifier.
  • Kotlin: IntArray(5) is the homogeneous primitive-int counterpart; Array<Int> boxes each element.

Bounds and indices from zero

Java numbers array slots starting at 0. An array of length n has legal indices 0, 1, ..., n - 1. The first element is at index 0; the last element is at index n - 1, never n. The expression arr.length gives n, so arr[arr.length - 1] is the last element and arr[arr.length] is always out of bounds.

Zero-based indexing is inherited from C (where the index is literally an offset added to a base address). Java keeps the C numbering but adds a runtime guard: any attempt to use an index outside 0..length-1 throws ArrayIndexOutOfBoundsException instead of silently reading or writing neighboring memory. This is the most useful runtime check in CS1, because the off-by-one error it catches is the most common bug in array code.

final int[] xs = {10, 20, 30, 40, 50};   // length 5
System.out.println(xs[0]);                // 10  (first)
System.out.println(xs[xs.length - 1]);    // 50  (last)
// System.out.println(xs[xs.length]);     // ArrayIndexOutOfBoundsException

In other languages

  • Python: also zero-based; xs[-1] is a Python convenience for "last element" that Java does not provide.
  • C: zero-based, but no bounds check: xs[xs_len] reads whatever byte happens to live just past the array. This is the source of the buffer-overrun bugs that Java's bounds check prevents.
  • Kotlin: zero-based; throws ArrayIndexOutOfBoundsException like Java.

Buffer overrun history (and why Java bounds-checks)

Why does Java throw ArrayIndexOutOfBoundsException instead of just returning whatever lives at the bad address? Because the languages that do not throw an exception: C and C++: produced a generation of catastrophic security bugs called buffer overruns (or buffer overflows). Writing past the end of an array in C silently overwrites adjacent memory; if an attacker controls the data being written, they can overwrite a return address or a function pointer and take over the program. The 1988 Morris worm (the first internet worm), the 2001 Code Red worm, and the 2003 SQL Slammer worm all exploited this exact class of bug. Java was designed in the early 1990s with this history in mind, and bounds-checking arrays was a deliberate language-design decision: every read and write of arr[i] is preceded by a runtime check that 0 <= i < arr.length. C# (Microsoft, 2000) made the same choice for the same reason.

The message for CS1 is short. The exception is a gift. It tells you exactly where the bug is. In C, the same bug would silently corrupt your program and possibly create a security hole. When you see ArrayIndexOutOfBoundsException, you are seeing the runtime save you from a Morris-worm-class bug.

final int[] xs = new int[5];
xs[10] = 99;   // Java: ArrayIndexOutOfBoundsException at runtime.
               // C equivalent: silently writes into whatever memory follows xs.

In other languages

  • C: no bounds check. xs[10] = 99; writes into whatever memory follows the array: sometimes a local variable, sometimes a return address. This is the bug that Morris/Code Red/SQL Slammer exploited.
  • Python: bounds-checked; raises IndexError. Same philosophy as Java.
  • Rust: bounds-checked at runtime by default; the compiler can elide the check when bounds are statically provable.

Tasks

Do each one, then check the box. Checking a box is you saying you did it. You can uncheck a box if you check it by accident.

  1. trace
    Show the answer

    5.

  2. trace
    Show the answer

    0 through 6 inclusive.

  3. trace
    Show the answer

    ArrayIndexOutOfBoundsException is thrown; nothing is written.

  4. trace
    Show the answer

    no: last legal index is 4.

Self check

Type what you think the answer is. Getting it wrong costs nothing and you can try as many times as you want.

Given int[] a = new int[7];, predict the result of a[7].

Practice, untimed

Open this whenever you want, before the tasks or after them. Nothing in this section is recorded and nothing here is timed.

  1. Declare and construct a double[] named prices with room for 10 values.write
    Show the answer

    final double[] prices = new double[10];.

  2. Write an expression for the last element of an array xs of unknown length.write
    Show the answer

    xs[xs.length - 1].

  3. Write an expression for the middle element of an odd-length array xs.write
    Show the answer

    xs[xs.length / 2] (integer division gives the middle slot for odd length).

  4. Given the equivalent C code, predict what happens.trace
    Show the answer

    writes into whatever memory follows the array (no exception, possibly silent corruption).

How this room finishes

This room is done when all 4 tasks are checked and the self check is answered.