Passing Arrays to Methods in Java
Passing Arrays to Methods in Java
When you pass an array to a method in Java, you are handing over a reference to the array — not a copy of all its elements. The method receives an address that points to the same array in memory. This means the method can read every element, modify any element, sort the array, or fill it with new values — and every change is immediately visible to the caller.
This behaviour is directly useful: methods can be given an array, do their work on it, and the results are reflected without needing to return anything. It also introduces risk: a method that was only supposed to read an array can accidentally mutate it. Understanding exactly what happens when an array is passed — and when those changes are or are not visible — is one of the most tested topics in Java interviews.
What Happens When an Array Is Passed
Java always passes arguments by value. For arrays, the value being passed is the reference — the memory address of the array object. Both the caller's variable and the method's parameter end up pointing to the same array in memory.
Caller creates: int[] marks = {85, 72, 91};
|
marks ─────────► [ 85 | 72 | 91 ] (on heap)
address: 0x5A2F
Passes to method: processMarks(marks)
|
arr ─────────► [ 85 | 72 | 91 ] (same address: 0x5A2F)
Both 'marks' and 'arr' point to the SAME array.
Changing arr[0] inside the method changes the same slot that marks[0] reads.
But if inside the method: arr = new int[]{1, 2, 3};
|
arr ─────────► [ 1 | 2 | 3 ] (new array, new address)
marks ─────────► [ 85 | 72 | 91 ] (unchanged — still original)
Three rules follow from this:
- ›Reading array elements inside a method works exactly as expected.
- ›Modifying array elements (
arr[i] = val) changes the original array — the caller sees this. - ›Reassigning the parameter (
arr = new int[]{}) has no effect on the caller's variable.
Reading an Array in a Method
The simplest case — a method that reads an array without modifying it.
1// File: ReadArrayDemo.java
2
3import java.util.Arrays;
4
5public class ReadArrayDemo {
6
7 // Reads array — no modification
8 public static int findMax(int[] numbers) {
9 if (numbers == null || numbers.length == 0) {
10 throw new IllegalArgumentException("Array must not be null or empty.");
11 }
12 int max = numbers[0];
13 for (int i = 1; i < numbers.length; i++) {
14 if (numbers[i] > max) max = numbers[i];
15 }
16 return max;
17 }
18
19 public static double calculateAverage(int[] numbers) {
20 if (numbers == null || numbers.length == 0) return 0.0;
21 int sum = 0;
22 for (int num : numbers) sum += num;
23 return (double) sum / numbers.length;
24 }
25
26 public static boolean containsValue(int[] arr, int target) {
27 for (int val : arr) {
28 if (val == target) return true;
29 }
30 return false;
31 }
32
33 public static void main(String[] args) {
34
35 int[] scores = {72, 88, 91, 65, 78, 95, 83};
36
37 System.out.println("Array : " + Arrays.toString(scores));
38 System.out.println("Max : " + findMax(scores));
39 System.out.printf("Average : %.2f%n", calculateAverage(scores));
40 System.out.println("Has 91? : " + containsValue(scores, 91));
41 System.out.println("Has 50? : " + containsValue(scores, 50));
42
43 // Original array is completely unchanged
44 System.out.println("After : " + Arrays.toString(scores));
45 }
46}Output:
Array : [72, 88, 91, 65, 78, 95, 83]
Max : 95
Average : 81.71
Has 91? : true
Has 50? : false
After : [72, 88, 91, 65, 78, 95, 83]
These methods only read the array — they never assign to arr[i]. The caller's array is untouched. Reading is always safe.
Modifying an Array in a Method
When a method writes to arr[i], it modifies the original array. The caller sees the change immediately after the method returns.
1// File: ModifyArrayDemo.java
2
3import java.util.Arrays;
4
5public class ModifyArrayDemo {
6
7 // Doubles every element — modifies the original
8 public static void doubleAll(int[] arr) {
9 for (int i = 0; i < arr.length; i++) {
10 arr[i] = arr[i] * 2;
11 }
12 }
13
14 // Sets elements below a threshold to zero
15 public static void zeroOutBelow(int[] arr, int threshold) {
16 for (int i = 0; i < arr.length; i++) {
17 if (arr[i] < threshold) arr[i] = 0;
18 }
19 }
20
21 // Swaps two elements inside the array
22 public static void swap(int[] arr, int i, int j) {
23 int temp = arr[i];
24 arr[i] = arr[j];
25 arr[j] = temp;
26 }
27
28 // Normalises all values to a 0-100 scale
29 public static void normalise(double[] arr) {
30 double max = arr[0];
31 for (double v : arr) if (v > max) max = v;
32 if (max == 0) return;
33 for (int i = 0; i < arr.length; i++) {
34 arr[i] = (arr[i] / max) * 100.0;
35 }
36 }
37
38 public static void main(String[] args) {
39
40 int[] prices = {150, 300, 75, 450, 200};
41 System.out.println("Before doubleAll : " + Arrays.toString(prices));
42 doubleAll(prices);
43 System.out.println("After doubleAll : " + Arrays.toString(prices));
44
45 System.out.println();
46
47 int[] marks = {85, 32, 91, 48, 72, 28, 95};
48 System.out.println("Before zeroOut : " + Arrays.toString(marks));
49 zeroOutBelow(marks, 50);
50 System.out.println("After zeroOut(<50): " + Arrays.toString(marks));
51
52 System.out.println();
53
54 int[] arr = {10, 20, 30, 40, 50};
55 System.out.println("Before swap(0,4) : " + Arrays.toString(arr));
56 swap(arr, 0, 4);
57 System.out.println("After swap(0,4) : " + Arrays.toString(arr));
58
59 System.out.println();
60
61 double[] sales = {4200, 8500, 6300, 3900, 7100};
62 System.out.println("Before normalise : " + Arrays.toString(sales));
63 normalise(sales);
64 System.out.printf("After normalise : ");
65 for (double s : sales) System.out.printf("%.1f ", s);
66 System.out.println();
67 }
68}Output:
Before doubleAll : [150, 300, 75, 450, 200]
After doubleAll : [300, 600, 150, 900, 400]
Before zeroOut : [85, 32, 91, 48, 72, 28, 95]
After zeroOut(<50): [85, 0, 91, 0, 72, 0, 95]
Before swap(0,4) : [10, 20, 30, 40, 50]
After swap(0,4) : [50, 20, 30, 40, 10]
Before normalise : [4200.0, 8500.0, 6300.0, 3900.0, 7100.0]
After normalise : 49.4 100.0 74.1 45.9 83.5
Every method modifies arr[i] directly. Because arr and the caller's variable point to the same array, every change is visible in the original variable after the method returns.
Reassignment Does Not Affect the Caller
This is the most misunderstood behaviour. Assigning arr = new int[]{...} inside a method only changes where the local parameter variable points. The caller's variable still holds the address of the original array.
1// File: ReassignmentDemo.java
2
3import java.util.Arrays;
4
5public class ReassignmentDemo {
6
7 // Attempts to replace the array — does NOT affect caller
8 public static void tryReplace(int[] arr) {
9 System.out.println(" Inside (before): " + Arrays.toString(arr));
10 arr = new int[]{1, 2, 3, 4, 5}; // local 'arr' now points to a NEW array
11 System.out.println(" Inside (after) : " + Arrays.toString(arr));
12 // caller's variable still points to the original
13 }
14
15 // Correctly replaces content by modifying elements in place
16 public static void fillWithValues(int[] arr, int[] newValues) {
17 int len = Math.min(arr.length, newValues.length);
18 for (int i = 0; i < len; i++) {
19 arr[i] = newValues[i]; // modifies the shared object — caller sees this
20 }
21 }
22
23 public static void main(String[] args) {
24
25 int[] data = {88, 72, 91, 65, 79};
26
27 System.out.println("=== Reassignment — no effect on caller ===");
28 System.out.println("Before: " + Arrays.toString(data));
29 tryReplace(data);
30 System.out.println("After : " + Arrays.toString(data)); // still original values
31
32 System.out.println();
33
34 System.out.println("=== In-place fill — visible to caller ===");
35 System.out.println("Before: " + Arrays.toString(data));
36 fillWithValues(data, new int[]{10, 20, 30, 40, 50});
37 System.out.println("After : " + Arrays.toString(data)); // all changed
38 }
39}Output:
=== Reassignment — no effect on caller ===
Before: [88, 72, 91, 65, 79]
Inside (before): [88, 72, 91, 65, 79]
Inside (after) : [1, 2, 3, 4, 5]
After : [88, 72, 91, 65, 79]
=== In-place fill — visible to caller ===
Before: [88, 72, 91, 65, 79]
After : [10, 20, 30, 40, 50]
tryReplace changes its local copy of the reference — but the caller's data variable still holds the original address. fillWithValues writes to each slot of the existing array — the caller's data reflects every change.
Returning Arrays From Methods
A method can create a new array, populate it, and return it to the caller. This is the correct pattern when a method should produce a new array without modifying the input.
1// File: ReturnArrayDemo.java
2
3import java.util.Arrays;
4
5public class ReturnArrayDemo {
6
7 // Creates and returns a new array — input unchanged
8 public static int[] getSortedCopy(int[] arr) {
9 int[] copy = Arrays.copyOf(arr, arr.length);
10 Arrays.sort(copy);
11 return copy;
12 }
13
14 // Returns only the elements that pass a condition
15 public static int[] filterAbove(int[] arr, int threshold) {
16 int count = 0;
17 for (int val : arr) if (val > threshold) count++;
18
19 int[] result = new int[count];
20 int idx = 0;
21 for (int val : arr) if (val > threshold) result[idx++] = val;
22
23 return result;
24 }
25
26 // Returns a new array that is the element-wise sum of two arrays
27 public static int[] addArrays(int[] a, int[] b) {
28 if (a.length != b.length) {
29 throw new IllegalArgumentException("Arrays must have equal length.");
30 }
31 int[] result = new int[a.length];
32 for (int i = 0; i < a.length; i++) {
33 result[i] = a[i] + b[i];
34 }
35 return result;
36 }
37
38 // Returns a reversed copy — input untouched
39 public static int[] reversed(int[] arr) {
40 int[] rev = new int[arr.length];
41 for (int i = 0; i < arr.length; i++) {
42 rev[i] = arr[arr.length - 1 - i];
43 }
44 return rev;
45 }
46
47 public static void main(String[] args) {
48
49 int[] marks = {72, 91, 55, 88, 63, 79, 97, 45};
50
51 System.out.println("Original : " + Arrays.toString(marks));
52
53 int[] sorted = getSortedCopy(marks);
54 System.out.println("Sorted copy : " + Arrays.toString(sorted));
55 System.out.println("Original after : " + Arrays.toString(marks)); // unchanged
56
57 System.out.println();
58
59 int[] passing = filterAbove(marks, 60);
60 System.out.println("Above 60 : " + Arrays.toString(passing));
61
62 System.out.println();
63
64 int[] weekA = {10, 20, 30, 40, 50};
65 int[] weekB = {15, 25, 35, 45, 55};
66 int[] total = addArrays(weekA, weekB);
67 System.out.println("Week A : " + Arrays.toString(weekA));
68 System.out.println("Week B : " + Arrays.toString(weekB));
69 System.out.println("Combined : " + Arrays.toString(total));
70
71 System.out.println();
72
73 int[] rev = reversed(marks);
74 System.out.println("Reversed copy : " + Arrays.toString(rev));
75 System.out.println("Original after : " + Arrays.toString(marks)); // unchanged
76 }
77}Output:
Original : [72, 91, 55, 88, 63, 79, 97, 45]
Sorted copy : [45, 55, 63, 72, 79, 88, 91, 97]
Original after : [72, 91, 55, 88, 63, 79, 97, 45]
Above 60 : [72, 91, 88, 63, 79, 97]
Week A : [10, 20, 30, 40, 50]
Week B : [15, 25, 35, 45, 55]
Combined : [25, 45, 65, 85, 105]
Reversed copy : [45, 97, 79, 63, 88, 55, 91, 72]
Original after : [72, 91, 55, 88, 63, 79, 97, 45]
These methods return new arrays without touching their inputs. getSortedCopy is the canonical pattern — copy with Arrays.copyOf, sort the copy, return it. The caller gets a sorted version; the original is preserved for name-index mapping, display, or further processing.
Passing 2D Arrays to Methods
A 2D array passed to a method follows the same rules. The method receives a reference to the outer array, which holds references to inner row arrays. Modifying cell values (arr[i][j] = val) is visible to the caller. Reassigning the parameter or a row (arr = ... or arr[i] = ...) affects only the local copy.
1// File: TwoDArrayMethodDemo.java
2
3import java.util.Arrays;
4
5public class TwoDArrayMethodDemo {
6
7 // Prints a 2D array — read-only, no mutation
8 public static void printGrid(int[][] grid, String label) {
9 System.out.println(label + ":");
10 for (int[] row : grid) {
11 System.out.print(" ");
12 for (int val : row) System.out.printf("%5d", val);
13 System.out.println();
14 }
15 }
16
17 // Multiplies every element by a scalar — mutates the original
18 public static void multiplyAll(int[][] matrix, int scalar) {
19 for (int i = 0; i < matrix.length; i++) {
20 for (int j = 0; j < matrix[i].length; j++) {
21 matrix[i][j] *= scalar;
22 }
23 }
24 }
25
26 // Returns a transposed copy — original untouched
27 public static int[][] transpose(int[][] m) {
28 int rows = m.length;
29 int cols = m[0].length;
30 int[][] result = new int[cols][rows];
31 for (int i = 0; i < rows; i++) {
32 for (int j = 0; j < cols; j++) {
33 result[j][i] = m[i][j];
34 }
35 }
36 return result;
37 }
38
39 // Fills the diagonal of a square matrix — mutates original
40 public static void setDiagonal(int[][] matrix, int value) {
41 int size = Math.min(matrix.length, matrix[0].length);
42 for (int i = 0; i < size; i++) {
43 matrix[i][i] = value; // changes cells in the shared array
44 }
45 }
46
47 public static void main(String[] args) {
48
49 int[][] marks = {
50 {72, 85, 90},
51 {88, 61, 78},
52 {95, 83, 70}
53 };
54
55 printGrid(marks, "Original");
56
57 System.out.println();
58 multiplyAll(marks, 2);
59 printGrid(marks, "After multiplyAll(2) — original mutated");
60
61 System.out.println();
62
63 int[][] original = {
64 {1, 2, 3},
65 {4, 5, 6}
66 };
67 int[][] transposed = transpose(original);
68 printGrid(original, "Original (2×3)");
69 printGrid(transposed, "Transposed (3×2)");
70
71 System.out.println();
72
73 int[][] identity = {
74 {0, 0, 0},
75 {0, 0, 0},
76 {0, 0, 0}
77 };
78 setDiagonal(identity, 1);
79 printGrid(identity, "Identity matrix after setDiagonal(1)");
80 }
81}Output:
Original:
72 85 90
88 61 78
95 83 70
After multiplyAll(2) — original mutated:
144 170 180
176 122 156
190 166 140
Original (2×3):
1 2 3
4 5 6
Transposed (3×2):
1 4
2 5
3 6
Identity matrix after setDiagonal(1):
1 0 0
0 1 0
0 0 1
multiplyAll and setDiagonal mutate cell values inside the shared 2D array — the caller sees the changes. transpose creates and returns a new 2D array — the original is untouched.
Mutation vs Reassignment — Comparison Table
| Action Inside Method | Effect on Caller's Array | Example |
|---|---|---|
Read element: x = arr[i] | None — read only | int max = arr[0] |
Modify element: arr[i] = val | Visible — same array object | arr[0] = 99 |
Sort: Arrays.sort(arr) | Visible — sorts the original | Arrays.sort(arr) |
Fill: Arrays.fill(arr, 0) | Visible — fills the original | Arrays.fill(arr, 0) |
Reassign parameter: arr = new int[]{} | Not visible — only local copy changes | arr = new int[5] |
Reassign row in 2D: arr[0] = new int[]{} | Not visible for the row reference | arr[0] = new int[3] |
Modify 2D cell: arr[i][j] = val | Visible — same array object | arr[0][0] = 1 |
Pass null: method(null) | No effect — null reference passed | processArray(null) |
Arrays.copyOf() inside method | Original unchanged — creates new array | return Arrays.copyOf(arr, n) |
Real-World Example — Student Report Processing System
The Business Problem
A school's result processing system receives student marks arrays from data entry. The system validates the input, computes statistics without modifying the original, applies a curve (grade boost) which does modify in-place, generates a sorted ranking copy for display, and identifies students who need intervention. Each operation is a separate method that correctly decides whether to mutate or create a copy.
1// File: ResultProcessor.java
2
3import java.util.Arrays;
4
5public class ResultProcessor {
6
7 private static final int PASS_MARK = 45;
8 private static final int DISTINCTION = 75;
9
10 // Read-only — validates without modifying
11 public static boolean isValidMarksArray(int[] marks) {
12 if (marks == null || marks.length == 0) return false;
13 for (int mark : marks) {
14 if (mark < 0 || mark > 100) return false;
15 }
16 return true;
17 }
18
19 // Read-only — computes stats without touching original
20 public static void printStatistics(String className, int[] marks) {
21 int sum = 0;
22 int max = marks[0];
23 int min = marks[0];
24 int pass = 0;
25 int dist = 0;
26
27 for (int mark : marks) {
28 sum += mark;
29 if (mark > max) max = mark;
30 if (mark < min) min = mark;
31 if (mark >= PASS_MARK) pass++;
32 if (mark >= DISTINCTION) dist++;
33 }
34
35 System.out.println("── " + className + " Statistics ──");
36 System.out.printf(" Students : %d%n", marks.length);
37 System.out.printf(" Average : %.2f%n", (double) sum / marks.length);
38 System.out.printf(" Highest : %d%n", max);
39 System.out.printf(" Lowest : %d%n", min);
40 System.out.printf(" Passed : %d/%d%n", pass, marks.length);
41 System.out.printf(" Distinction: %d/%d%n", dist, marks.length);
42 }
43
44 // Mutates in-place — applies a grace curve to original array
45 public static void applyGraceCurve(int[] marks, int graceMarks) {
46 for (int i = 0; i < marks.length; i++) {
47 marks[i] = Math.min(100, marks[i] + graceMarks); // cap at 100
48 }
49 }
50
51 // Returns new array — sorted copy for ranking display
52 public static int[] getRankedMarks(int[] marks) {
53 int[] copy = Arrays.copyOf(marks, marks.length);
54 Arrays.sort(copy);
55 // reverse to get descending order
56 for (int i = 0; i < copy.length / 2; i++) {
57 int temp = copy[i];
58 copy[i] = copy[copy.length - 1 - i];
59 copy[copy.length - 1 - i] = temp;
60 }
61 return copy;
62 }
63
64 // Returns new array — students who failed (marks < PASS_MARK)
65 public static int[] getFailedMarks(int[] marks) {
66 int count = 0;
67 for (int m : marks) if (m < PASS_MARK) count++;
68
69 int[] failed = new int[count];
70 int idx = 0;
71 for (int m : marks) if (m < PASS_MARK) failed[idx++] = m;
72 return failed;
73 }
74}1// File: SchoolResultDemo.java
2
3import java.util.Arrays;
4
5public class SchoolResultDemo {
6
7 public static void main(String[] args) {
8
9 String[] students = {
10 "Priya", "Rohan", "Sneha", "Karan",
11 "Ananya", "Deepak", "Meera", "Suresh"
12 };
13
14 int[] marks = {72, 38, 91, 42, 85, 65, 55, 88};
15
16 System.out.println("=== Raw Marks ===");
17 System.out.println(Arrays.toString(marks));
18
19 // Step 1 — Validate
20 boolean valid = ResultProcessor.isValidMarksArray(marks);
21 System.out.println("\n[1] Valid marks array: " + valid);
22
23 // Step 2 — Statistics (read-only, original unchanged)
24 System.out.println();
25 ResultProcessor.printStatistics("Class X-A", marks);
26 System.out.println("\nAfter stats — original: " + Arrays.toString(marks));
27
28 // Step 3 — Failed students (returns new array)
29 int[] failedMarks = ResultProcessor.getFailedMarks(marks);
30 System.out.println("\n[3] Failed marks: " + Arrays.toString(failedMarks));
31
32 // Step 4 — Apply grace curve (mutates original)
33 System.out.println("\n[4] Applying 5-mark grace curve...");
34 System.out.println("Before: " + Arrays.toString(marks));
35 ResultProcessor.applyGraceCurve(marks, 5);
36 System.out.println("After : " + Arrays.toString(marks));
37
38 // Step 5 — Ranking (returns sorted copy, original untouched)
39 int[] ranked = ResultProcessor.getRankedMarks(marks);
40 System.out.println("\n[5] Ranked (highest first): " + Arrays.toString(ranked));
41 System.out.println(" Original still : " + Arrays.toString(marks));
42
43 // Step 6 — Final report with names
44 System.out.println("\n=== Final Grade Report ===");
45 System.out.printf("%-12s %5s %s%n", "Student", "Marks", "Result");
46 System.out.println("─".repeat(30));
47 for (int i = 0; i < students.length; i++) {
48 String result = marks[i] >= 75 ? "DISTINCTION"
49 : marks[i] >= 45 ? "PASS"
50 : "FAIL";
51 System.out.printf("%-12s %5d %s%n",
52 students[i], marks[i], result);
53 }
54 }
55}Output:
=== Raw Marks ===
[72, 38, 91, 42, 85, 65, 55, 88]
[1] Valid marks array: true
── Class X-A Statistics ──
Students : 8
Average : 67.00
Highest : 91
Lowest : 38
Passed : 6/8
Distinction: 4/8
After stats — original: [72, 38, 91, 42, 85, 65, 55, 88]
[3] Failed marks: [38, 42]
[4] Applying 5-mark grace curve...
Before: [72, 38, 91, 42, 85, 65, 55, 88]
After : [77, 43, 96, 47, 90, 70, 60, 93]
[5] Ranked (highest first): [96, 93, 90, 77, 70, 60, 47, 43]
Original still : [77, 43, 96, 47, 90, 70, 60, 93]
=== Final Grade Report ===
Student Marks Result
──────────────────────────────
Priya 77 DISTINCTION
Rohan 43 FAIL
Sneha 96 DISTINCTION
Karan 47 PASS
Ananya 90 DISTINCTION
Deepak 70 PASS
Meera 60 PASS
Suresh 93 DISTINCTION
The design is deliberate: printStatistics and isValidMarksArray are read-only — they never touch a single array slot. applyGraceCurve intentionally mutates in-place — the change is the feature. getRankedMarks and getFailedMarks return new arrays — the original order is preserved for the name-to-mark mapping that the final report depends on.
Best Practices
Document whether a method mutates its array parameter. A method named sort(int[] arr) clearly mutates. A method named getAverage(int[] arr) clearly does not. When the intent is ambiguous — like process(int[] arr) — add a Javadoc comment: @param arr — this array is modified in place. During code reviews at product companies, undocumented mutations on shared arrays are flagged as bugs before they cause them.
Return a new array when the input should remain unchanged. If the caller needs the original array intact after the method runs — for display, further processing, or name-index mapping — create a copy inside the method using Arrays.copyOf() and return the modified copy. Never silently modify a parameter unless that is the explicit purpose of the method.
Validate null and empty arrays at the start of every method. if (arr == null || arr.length == 0) is the standard guard. Without it, arr[0] on an empty array throws ArrayIndexOutOfBoundsException and arr.length on a null array throws NullPointerException. Both are preventable with one check.
Use Arrays.copyOf() inside the method to isolate mutations. If a method needs to sort or rearrange its input without affecting the caller, copy it with int[] work = Arrays.copyOf(arr, arr.length) and operate on work. Return the result. The caller never sees internal rearrangements.
Common Mistakes
Mistake 1 — Sorting the Original When Only a Sorted View Was Needed
1public static void printRanking(String[] names, int[] marks) {
2 // Sorts the original marks — destroys the name-to-mark mapping
3 Arrays.sort(marks);
4 for (int i = marks.length - 1; i >= 0; i--) {
5 System.out.println(marks[i]); // names no longer align with marks
6 }
7}
8
9// Fix — sort a copy
10public static void printRanking(String[] names, int[] marks) {
11 int[] copy = Arrays.copyOf(marks, marks.length);
12 Arrays.sort(copy);
13 for (int i = copy.length - 1; i >= 0; i--) {
14 System.out.println(copy[i]);
15 }
16}This is the most common unintentional mutation in beginner code. The method was meant to display rankings — but it permanently rearranged the marks array, destroying its alignment with the names array.
Mistake 2 — Expecting Null to Be Handled Automatically
1public static int sum(int[] arr) {
2 int total = 0;
3 for (int val : arr) total += val; // NullPointerException if arr is null
4 return total;
5}
6
7// A method that receives an array should validate it
8public static int sum(int[] arr) {
9 if (arr == null) return 0; // or throw IllegalArgumentException
10 int total = 0;
11 for (int val : arr) total += val;
12 return total;
13}Mistake 3 — Reassigning the Parameter and Expecting Caller to See It
1public static void resetArray(int[] arr) {
2 arr = new int[arr.length]; // local copy points to new array — caller unchanged
3 System.out.println("Looks reset here: " + java.util.Arrays.toString(arr));
4}
5
6int[] data = {1, 2, 3, 4, 5};
7resetArray(data);
8System.out.println("Still original: " + java.util.Arrays.toString(data)); // unchanged
9
10// Fix — modify elements in place
11public static void resetArray(int[] arr) {
12 java.util.Arrays.fill(arr, 0); // fills the shared array — caller sees this
13}Mistake 4 — Modifying a 2D Array Row by Reassignment
1public static void clearFirstRow(int[][] matrix) {
2 matrix[0] = new int[matrix[0].length]; // replaces LOCAL row reference
3 // caller's matrix[0] still points to the original row — unchanged
4}
5
6// Fix — fill the existing row in place
7public static void clearFirstRow(int[][] matrix) {
8 java.util.Arrays.fill(matrix[0], 0); // modifies the existing shared row
9}Interview Questions
Q1. What happens when an array is passed to a method in Java?
Java passes the array by value — specifically, by value of the reference. The method receives a copy of the memory address pointing to the array. Both the caller's variable and the method's parameter refer to the same array object on the heap. Mutations to array elements (arr[i] = val) through the parameter are visible to the caller. Reassigning the parameter to a new array (arr = new int[]{}) only changes the local copy — the caller's variable still points to the original.
Q2. What is the difference between mutating an array in a method and reassigning the array parameter?
Mutating means changing the content of the existing array object — writing to arr[i], calling Arrays.sort(arr), or Arrays.fill(arr, 0). Because both caller and method reference the same object, mutations are visible to the caller. Reassigning means making the local parameter variable point to a different array — arr = new int[5]. This only changes where the local variable points; the caller's reference is unaffected and still holds the original address.
Q3. How do you pass an array to a method without allowing the method to modify the original?
Java has no built-in read-only array parameter. The two approaches are: pass a copy — method(Arrays.copyOf(original, original.length)) — so the method modifies the copy, not the original; or create the copy inside the method if the method is responsible for isolation. Using an immutable wrapper (Collections.unmodifiableList) does not apply to arrays directly. For sensitive scenarios, making a copy at the call site and passing the copy is the most explicit and clear approach.
Q4. Can a method return more than one array in Java?
Not directly — a method has a single return type. To return multiple arrays, wrap them in an object. A record is the cleanest option: record SplitResult(int[] passed, int[] failed) {}. The method returns a SplitResult and the caller accesses both arrays via result.passed() and result.failed(). A custom class works too. Returning a 2D array where each row is a result is also used but less readable.
Q5. What happens when a 2D array is passed to a method?
The method receives a copy of the reference to the outer array. This outer array holds references to inner row arrays. Modifying cell values — arr[i][j] = val — changes the shared object and is visible to the caller. Replacing a row — arr[i] = new int[]{} — only changes the local copy of that row's reference in the outer array; the caller's row reference is unaffected. Replacing the outer array — arr = new int[][]{} — has no effect on the caller.
Q6. Why should you validate an array parameter for null at the start of a method?
A null array passed to a method will throw NullPointerException the moment any operation is performed on it — .length, arr[i], or iterating with for-each. Since the JVM error message just says "null" without indicating which parameter was null or which method received it, debugging becomes harder. Checking if (arr == null) at the start produces a controlled failure with a meaningful message, or a graceful fallback. Combined with arr.length == 0, this guard prevents the two most common array method crashes.
FAQs
Can a method receive an array and add elements to it?
No. Arrays have a fixed size set at creation. A method receiving an array cannot add elements — there are no empty slots to fill beyond arr.length - 1. The method can overwrite existing elements, or return a new larger array created with Arrays.copyOf(arr, arr.length + n). For dynamic addition, ArrayList is the correct type to pass instead of a raw array.
Does sorting an array inside a method affect the original?
Yes. Arrays.sort(arr) sorts the elements of the array object that arr points to — which is the same object the caller's variable points to. The sorted order is permanent and visible to the caller after the method returns. If you need to sort without modifying the original, copy first: int[] copy = Arrays.copyOf(arr, arr.length); Arrays.sort(copy);
Can varargs and arrays be used interchangeably?
A varargs method void process(int... arr) accepts the same call as void process(int[] arr) when you pass an existing array — the compiler treats a varargs parameter as a regular int[] inside the method. The difference is at the call site: varargs lets callers pass individual values directly — process(1, 2, 3) — without constructing an array. An array parameter forces the caller to build the array first. Inside both methods, the parameter behaves identically as int[].
How do you copy a 2D array to prevent mutations from propagating?
Arrays.copyOf(matrix, matrix.length) copies the outer array but shares inner row references — a shallow copy. Mutations to cell values still propagate because the rows are shared. For a true deep copy, copy each row: for (int i = 0; i < matrix.length; i++) copy[i] = Arrays.copyOf(matrix[i], matrix[i].length). This creates independent row arrays — mutations to one copy do not affect the other.
Is it better to mutate an array parameter or return a new array?
Returning a new array is generally safer and more predictable — the caller retains control of the original and is not surprised by side effects. Mutating in-place is acceptable when the method's explicit purpose is to transform the array, the name clearly signals mutation (sort, fill, update), and performance matters (avoiding array allocation). In production code, pure functions that return new arrays are easier to test, compose, and reason about. In-place mutation is used when performance is the priority or when the API contract explicitly states it.
Summary
Passing an array to a method in Java passes the reference — the memory address — by value. Element mutations through that reference are visible to the caller. Reference reassignment is invisible to the caller. This single rule explains every behaviour you will encounter when writing methods that work with arrays.
The production rule that prevents most bugs: decide upfront whether a method should mutate its input or return a new array, and make that decision visible through the method name and documentation. sortInPlace(int[] arr) signals mutation. getSortedCopy(int[] arr) signals that the original is preserved. When callers can predict the effect of a method call from its name alone, bugs from unintended array mutation disappear.
For interviews, be ready to explain the difference between element mutation and parameter reassignment with a concrete memory diagram, demonstrate the pattern of copying before sorting, explain how to validate null and empty arrays, and describe when to mutate in place versus return a new array.
What to Read Next
| Topic | Link |
|---|---|
| How Java's pass by value rule applies to all types — primitives and objects | Java Pass by Value → |
| How the Arrays class provides copyOf, sort, fill, and stream for array operations | Java Arrays Class Methods → |
| How ArrayList accepts dynamic additions unlike fixed arrays | Java ArrayList → |
| How multi-dimensional arrays behave when passed to methods | Java Multi-dimensional Arrays → |
| How varargs let methods accept a variable number of array-like arguments | Java Varargs → |