JAVA / LOOPS AND ARRAYS
Creating, indexing and initialising arrays
Allocate Java arrays with new or a brace initialiser, index them safely, and predict the starting value of every element.
What you will learn
- Allocate with new int[n]; read the size back from the final field arr.length
- Index by offset: the valid range is 0 to arr.length - 1, last is arr[arr.length - 1]
- Predict defaults: 0 for int, 0.0 for double, false for boolean, null for references
- Use {1, 2, 3} only in a declaration; use new int[]{1, 2, 3} in any other position
Understanding Creating, indexing and initialising arrays
An array in Java is one block of storage holding a fixed number of slots that all share a single declared element type. The line int[] scores = new int[4] creates two distinct things: the variable scores, which holds a reference, and the four-slot block that new allocates on the heap. The size expression is evaluated once, at allocation time, and then frozen; the block itself records how long it is, which is why you read the size back as the final field scores.length instead of tracking it in a separate variable.
An index is an offset from the start of that block rather than a position count, which is why the first element is arr[0] and the last is arr[arr.length - 1]. Every read and every write is bounds-checked while the program runs, so an index below 0 or at length or above throws ArrayIndexOutOfBoundsException instead of quietly reading storage that belongs to something else. The index may be any int expression, so arr[i + 1] and arr[arr.length - 2] are ordinary accesses, and the check applies to whatever value the expression produces.
You never get an unset element: new fills the entire block with the zero pattern for the element type, so numeric slots start at 0 or 0.0, boolean slots at false, char slots at the character whose code is 0, and reference slots at null. That is a stronger guarantee than the compiler gives a local variable, which it refuses to let you read before you assign it. When the contents are already known, int[] a = {3, 5, 8} allocates and fills in one step and takes the length from the number of elements listed; the bare braces are legal only in a declaration, so in an assignment, an argument, or a return you write the full form new int[]{3, 5, 8}.
public class ArrayBasics {
public static void main(String[] args) {
int[] scores = new int[4];
System.out.println("scores.length = " + scores.length);
System.out.println("scores[0] straight after new = " + scores[0]);
scores[0] = 91;
scores[3] = 68;
System.out.println("first = " + scores[0] + ", last = " + scores[scores.length - 1]);
String[] names = new String[2];
System.out.println("names[1] straight after new = " + names[1]);
double[] weights = { 1.5, 2.25, 3.0 };
System.out.println("weights[1] = " + weights[1] + ", count = " + weights.length);
try {
System.out.println(weights[weights.length]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("weights[" + weights.length + "] threw " + e.getClass().getSimpleName());
}
}
}An array is a fixed-length, automatically zero-filled block of same-typed slots addressed by offsets 0 through length - 1, and the variable only holds a reference to that block.
Worked examples
What new leaves in each slot
Shows the starting value new writes into slots of four different element types.
public class Defaults {
public static void main(String[] args) {
boolean[] flags = new boolean[2];
char[] initials = new char[2];
double[] rates = new double[2];
Object[] slots = new Object[2];
System.out.println("boolean -> " + flags[0]);
System.out.println("char as int -> " + (int) initials[0]);
System.out.println("double -> " + rates[0]);
System.out.println("Object -> " + slots[0]);
}
}Example explained
Line 1new boolean[2] zero-fills the block, and the zero pattern for boolean prints as false.
Line 2The char default is the character with code 0, which is invisible on a console, so the cast to int is what makes it observable.
Line 3rates[0] prints 0.0 rather than 0 because the slot really holds a double, and that is how a double is formatted.
Line 4Object[] slots are references, so they start as null: the array exists but no objects have been created for it.
Three ways to build the same array
Compares new plus assignments, the brace initialiser, and the new int[]{...} form used outside a declaration.
public class ArrayShapes {
public static void main(String[] args) {
int[] a = new int[3];
a[0] = 10;
a[1] = 20;
a[2] = 30;
int[] b = {10, 20, 30};
int[] c;
c = new int[]{10, 20, 30};
System.out.println(a[2] + " " + b[2] + " " + c[2]);
System.out.println(sum(new int[]{1, 2, 3, 4}));
int n = 2 + 3;
int[] sized = new int[n];
System.out.println("sized.length = " + sized.length);
}
static int sum(int[] values) {
return values[0] + values[1] + values[2] + values[3];
}
}Example explained
Line 1int[] b = {10, 20, 30}; needs no size because the compiler counts the listed elements and allocates a block of exactly three.
Line 2c is assigned after its declaration, so the bare braces are not allowed and new int[]{...} is required.
Line 3The same full form lets an array be built directly in the argument position of sum, with no named variable at all.
Line 4new int[n] proves the length can come from a runtime int expression; once allocated at 5 it stays 5.
Zero length is fine, negative is not
Demonstrates that an empty array is a legal object while a negative size fails only when the program runs.
public class Sizes {
public static void main(String[] args) {
int[] empty = new int[0];
System.out.println("empty.length = " + empty.length);
int wanted = -1;
try {
int[] bad = new int[wanted];
System.out.println(bad.length);
} catch (NegativeArraySizeException e) {
System.out.println("cannot allocate " + wanted + " slots");
}
}
}Example explained
Line 1new int[0] is a real array object with no slots, so empty.length is 0 and every index is out of bounds.
Line 2The size argument is checked when new executes, not when the code is compiled, so a negative size is a runtime failure.
Line 3NegativeArraySizeException is separate from ArrayIndexOutOfBoundsException: one is about creating the block, the other about addressing it.
Important notes
Both int[] a and int a[] compile, but in int a[], b; only a is an array while b is a plain int, which is why the brackets belong on the type.
The length is fixed at allocation, so making an array bigger always means allocating a new block; the original one is unaffected by that decision.
Common mistakes
Writing arr.length() instead of arr.length: on arrays the size is a final field, not a method, so the code will not compile; only String has length().
Reaching for the last element with arr[arr.length]: that offset is one slot past the block, so the bounds check throws ArrayIndexOutOfBoundsException at run time.
Assuming new String[3] contains three empty strings and calling a method on element 0, which throws NullPointerException because reference slots start at null.
Try it yourself
Change, predict, then run
Declare String[] week = new String[3], print week[0] before assigning anything, then fill all three slots with day names and print week[week.length - 1]. Finish by reading week[3] and noting which exception the bounds check reports.
Open the Java workspaceCheck your understanding
A program runs double[] temps = new double[3]; and then immediately prints temps[2] followed by temps[3]. What happens?
- It prints 0.0, then throws ArrayIndexOutOfBoundsException
- It does not compile, because temps has not been given any values yet
- It prints 0, then prints null
- It prints 0.0 twice, because any slot that was never assigned reads as zero
Show answer
new double[3] allocates three slots and zero-fills them, so temps[2] is a genuine slot holding 0.0, while index 3 sits one past the last valid offset of 2 and the runtime bounds check throws. Option 1 is tempting because the compiler does block reading an unassigned local variable, but that rule covers locals only: new always initialises every element it allocates, so the array itself is fully usable straight away.