Pass-by-value in Java

Why can a method change your array but not your int? Why does a swap method do nothing? Java has one simple rule: every argument is copied. For objects, the copy is the reference, so both sides reach the same object. You'll see exactly what is copied, why mutating works and reassigning doesn't, how Strings fit in, and then practise with 11 problems.

Module 5 · Methods⏱ 22 minBeginner11 practice problems
1 The story

The swap that did nothing

Riya is writing a seat-exchange feature for the college bus. Two students want to swap seats, so she writes swap(seatA, seatB). Inside the method she prints the values: swapped. Back in main: unchanged. Yet last week, a method that changed an array's elements worked perfectly.

int: you hand over a photocopy main: seatA 12 copy swap: a 12 → 27 The method changes its copy. main still has 12. array: you hand over a copy of the address main: arr method: a one array object [27, 12] Both copies lead to the same object, so changes are shared. myphatshala.in
Riya

Sir, my swap method swaps the numbers inside, but main doesn't see it. Is Java passing them by reference or not?

Arjun Sir

Never by reference. Java always passes a copy. For an int, the copy is the number itself, so the method only changes its own copy.

Riya

Then how did my array method change the marks?

Arjun Sir

For an array, the variable holds a reference, so the copy is a copy of the reference. Both point to the same array. Change an element and everyone sees it. But point the parameter at a new array, and only the method's copy moves. That one rule explains everything today.

2 Definition

What does pass-by-value mean?

Formal definition (write this in exams)

Pass-by-value means that when a method is called, the value of each argument is copied into a new parameter variable in the method's stack frame. Java uses pass-by-value for all arguments. For a primitive, the value copied is the data itself. For an object or array, the value copied is the reference (the object's address); the object itself is not copied. Hence a method can modify the state of an object it receives, but assigning a new object to the parameter never affects the caller's variable.

In simple words

Giving a method an int is like giving someone a photocopy of a number: they can scribble on it, your original stays clean. Giving a method an array is like giving someone a copy of your house address: if they paint the house, you'll see it; if they cross out the address and write a different one, your house is unaffected.

Words you'll meet in this lesson

Pass-by-valueThe argument's value is copied into the parameter.
Pass-by-referenceThe parameter would be an alias of the caller's variable. Java doesn't do this.
ReferenceThe value stored in an object variable: where the object lives on the heap.
MutateChange the contents of an object: a[0] = 5, sb.append("x").
ReassignMake a variable refer to a different object: a = new int[3].
Side effectA change a method makes that is visible outside it, beyond its return value.

The rule, case by case

Primitive (int, double…)The number is copied. Changing the parameter never affects the caller.
Array or object: mutatea[0] = 5; changes the shared object. The caller sees it.
Array or object: reassigna = new int[3]; moves only the parameter. The caller doesn't see it.
StringImmutable, so it can't be mutated; s = s + "!" is a reassignment. The caller never sees a change.
StringBuilderMutable: sb.append("!") inside a method is visible to the caller (Lesson 4.6).
To give back a new valuereturn it, and let the caller assign it: x = f(x);

Exam tip: the one-line answer is "Java is always pass-by-value; for objects, the reference is passed by value." If asked to prove it, use the swap example: a swap(Integer a, Integer b) or swap(int[] a, int[] b) that reassigns its parameters can't swap the caller's variables.

3 Types and ways to use it

Three things a method can do with an argument

Change a primitive copy

n = n + 5;
Only the method's copy changes. The caller is unaffected.

Not visible

Mutate a shared object

a[0] = 99;
Both references reach the same object, so the caller sees the change.

Visible

Reassign the parameter

a = new int[]{-1};
The method's reference now points elsewhere. The caller still has the old object.

Not visible
public class Main {
    static void change(int n, int[] a) {
        n = 50;              // changes the copy only
        a[0] = 99;           // changes the shared array
        a = new int[]{-1};   // moves the copy of the reference only
    }

    public static void main(String[] args) {
        int x = 10;
        int[] arr = {10};
        change(x, arr);
        System.out.println("x = " + x + ", arr[0] = " + arr[0]);
    }
}
x = 10, arr[0] = 99

Pass-by-value vs pass-by-reference

Java (pass-by-value)True pass-by-reference (e.g. C++ int&)
What the parameter isA new variable holding a copyAnother name for the caller's variable
swap(a, b) on two intsCan't swap the caller's variablesSwaps them
Mutating an objectVisible (shared object)Visible
Assigning a new object to the parameterNot visible to the callerReplaces the caller's object
How to send a new value backreturn itAssign to the parameter

Common misconception: "objects are passed by reference in Java". Not quite: the reference is passed by value. If objects were truly passed by reference, a = new int[]{-1}; inside the method would replace the caller's array too. It doesn't.

4 Step by step

What happens to each argument during a call

  1. The caller's variables

    int x = 10;
    int[] arr = {10};
    In main's frame, x holds the number 10 directly. arr holds a reference; the array itself is an object on the heap (Lesson 4.1).
  2. The call copies both values

    change(x, arr);
    A new frame is pushed. n gets a copy of 10. a gets a copy of the reference, so it points to the same array as arr. No array is copied.
  3. Changing the primitive copy

    n = 50;
    Only the slot in change's frame is overwritten. main's x lives in a different frame and stays 10.
  4. Mutating through the reference

    a[0] = 99;
    The JVM follows the reference in a to the heap and writes into the array. Since arr points to that same array, main will see 99.
  5. Reassigning the parameter, then returning

    a = new int[]{-1};
    A new array is created and only a is pointed at it. When the method returns, its frame is popped, nothing refers to the new array any more, and the garbage collector can reclaim it. arr was never touched.
5 See it happen

Copies in, shared object changed: both views

Left: each line and its effect. Right: the two frames on the stack and the arrays on the heap.

Calling change(x, arr) 6 steps
What you see int x = 10; int[] arr = {10}; change(x, arr); n = 50; a[0] = 99; a = new int[]{-1}; println("x = " + x + ", arr[0] = " + arr[0]); // x = 10 // arr → [10] // n gets a copy of 10 // a gets a copy of the // reference in arr // n = 50 // x is still 10 // shared array → [99] // arr[0] is 99 now too // a → a new array [-1] // arr still → [99] x = 10, arr[0] = 99 One int, one array. Both arguments are copied. The int copy changes. x doesn't. The array changes for everyone. Only the method's arrow moves. x unchanged; arr[0] changed. myphatshala.in
Behind the screen Stack Heap main x = 10 arr change a n = 10 n = 50 frame popped #1 int[1] 10 99 #2 int[1] -1 #2: nobody refers to it now x holds 10 itself; arr holds a reference. Two arrows to one array: the reference was copied. n lives in change's frame; x is untouched. The write goes through a to the shared #1. a moved to #2; arr still points to #1. Frame gone: n and a vanish, #2 is garbage. myphatshala.in
6 Real-world use

Pass-by-value in real systems

Knowing exactly what a method can and can't change is how developers avoid bugs where data changes "by itself", or doesn't change when it should.

Watch it with an example: three ways a discount method can go wrong (or right)

Java always passes a copy. For an int that's a copy of the number; for an object it's a copy of the reference. Watch what each method can and can't change.

Copy of a value, copy of a reference 6 steps
Animated example int price = 500; Cart cart = new Cart(500); cut(price); // price = price - 50 inside // main's price is still 500 cut(cart); // c.total = c.total - 50 reset(cart); // c = new Cart(0) // cart still → #1, total 450 main price = 500 cart → #1 method frame p = 450 ← own copy c → #1 c → #2 #1 Cart total = 500 total = 450 #2 Cart frame gone total = 0 myphatshala.in
sorted in place

1. In-place library methods

Arrays.sort(a) and Arrays.fill(a, 0) return nothing. They work because they receive a reference to your array and rearrange or overwrite its elements directly.

Your array→Arrays.sort(a)→Same array, sorted
copy

2. Defensive copies

A banking class that receives an array of interest rates copies it before storing, and returns copies from getters. Otherwise any caller holding the same reference could change the bank's internal data (see the scenario in Lesson 4.1).

Caller's array→Arrays.copyOf(rates, n)→Private, safe copy
total = addTax(total);x = f(x)

3. Returning new values

Methods that calculate (tax, discount, formatted text) take inputs and return a result, leaving their inputs untouched. Callers write total = addTax(total);. This style is easier to test and safe to reuse.

Old value→return newValue;→Caller assigns it
"Meera"immutable: safe to share

4. Why Strings are safe to pass around

Because a String can't be mutated, passing a customer's name or an account number to any method is safe: no method can change the caller's String. This is one reason Strings are used for IDs and keys.

String id→any method→id unchanged

Examples are simplified. The same rule applies to every object you'll create in Module 6: a method receives a copy of the reference, never a copy of the object.

7 Run the code

Riya's seat swap: the failing and the working version

import java.util.Arrays;

public class Main {
    static void swap(int a, int b) {
        int temp = a;
        a = b;
        b = temp;
        System.out.println("Inside swap: a = " + a + ", b = " + b);
    }

    static void swapItems(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    public static void main(String[] args) {
        int x = 1, y = 2;
        swap(x, y);
        System.out.println("After swap: x = " + x + ", y = " + y);

        int[] seats = {1, 2};
        swapItems(seats, 0, 1);
        System.out.println("After swapItems: " + Arrays.toString(seats));
    }
}
Inside swap: a = 2, b = 1 After swap: x = 1, y = 2 After swapItems: [2, 1]
  • swap(int a, int b)a and b are copies of x and y. Swapping copies leaves the originals as they were.
  • "Inside swap: a = 2, b = 1"Proof that the swap itself works; it just happens in the wrong frame.
  • swapItems(int[] arr, int i, int j)Receives a copy of the reference, so it changes the one array that main also uses.
  • [2, 1]The working way to "swap" for a caller: keep the values inside an object both sides share.
8 Line by line

Mutate, then reassign

Watch the arrows: bal and a start at the same array (#1). One line changes #1, the next moves only a.

Worked examples: real problems, solved and traced in memory

Three bugs and fixes that come straight from pass-by-value. Run each program, then step through it and compare the variables in each frame with the objects (#n) on the heap: copies of values on the left, shared objects on the right.

Example 1Banking · interest

The interest that never reached the balance

Problem statement

A developer wrote addInterest(balance) to add 5% to a balance, but the balance printed afterwards is unchanged. Show the bug, then fix it by returning the new value.

Input
balance = 20000.0, rate = 5%
Expected output
After addInterest: 20000.0
After withInterest: 21000.0

Approach

  1. Call the buggy void method and watch its parameter change while main's variable stays the same.
  2. Write a version that returns the new balance.
  3. Assign the returned value back in main: balance = withInterest(balance);.

Solution: run it live

What happens in memory, step by step

Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.

Example 2Retail · price list

A method that discounts the caller's array

Problem statement

During a sale, every price in the shop's price array must drop by 10%. Write applySale(int[] prices) that changes the array itself, and print the prices afterwards.

Input
prices = {200, 550, 90}
Expected output
[180, 495, 81]

Approach

  1. The method receives a copy of the reference, so it points to the same array object as main.
  2. Loop with an index and overwrite each element: prices[i] = prices[i] * 90 / 100.
  3. No return is needed: main's variable still refers to the same, now-changed, array.

Solution: run it live

What happens in memory, step by step

Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.

Example 3HR · employee records

Reassigning a parameter vs changing the object

Problem statement

An HR tool keeps employee codes in a String[]. One method tries to "replace" the whole array; another updates one element. Predict which change main will see.

Input
codes = {"E01", "E02"}
Expected output
[M01, E02]

Approach

  1. replaceAll assigns a brand-new array to its parameter: only its own copy of the reference moves.
  2. renameFirst writes into the existing array through the reference: the shared object changes.
  3. Compare the heap: which object does main's codes point to at the end?

Solution: run it live

What happens in memory, step by step

Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.

9 Quiz and revision

Check yourself

Quick revision

Lesson 5.3 in one screen

  • Java is always pass-by-value: every argument is copied into a new parameter.
  • Primitive argument → the number is copied; the caller never sees changes.
  • Object/array argument → the reference is copied; the object is shared, not copied.
  • Mutating the object (a[0] = 5) is visible to the caller.
  • Reassigning the parameter (a = new …) is not.
  • Strings are immutable, so a method can never change the caller's String.
  • To hand back a new value, return it and assign: x = f(x);

Interview and viva questions

Is Java pass-by-value or pass-by-reference?

Always pass-by-value. For primitives, the value is the data. For objects and arrays, the value is a reference, which is copied. Because both copies point to the same object, mutations are visible, but reassigning the parameter never changes the caller's variable, which proves it isn't pass-by-reference.

How would you write a method that swaps two ints for the caller?

You can't with two int parameters. Options: pass an array and two indexes and swap the elements, return the swapped pair in an array, or keep the values in an object and swap its fields. In practice, code is written so a swap isn't needed across a method boundary.

If a method receives a String and calls s.toUpperCase(), why doesn't the caller's String change?

toUpperCase() returns a new String; it never modifies the original, because Strings are immutable. Even s = s.toUpperCase() only reassigns the method's own copy of the reference. Return the new String to the caller instead.

Does passing a large array to a method copy the whole array?

No. Only the reference is copied, which is a few bytes whatever the array's size. That's why passing big arrays or objects to methods is cheap, and also why a method can accidentally modify them.

What is a side effect, and why should methods document them?

A side effect is any change a method makes beyond returning a value, such as modifying an array it received. Callers can't see side effects from the call alone, so a method like sortInPlace or applySaleTo should make it obvious in its name and documentation. Methods without side effects are easier to test and reuse.

Practice problems

For each problem, ask one question before coding: does the method need to change the caller's data (then it must change an object, or return a new value), or only read it? The last three are real business tasks.

P1Easy

Fix the bonus that never arrives

The method static void addGrace(int marks) { marks = marks + 5; } is supposed to add 5 grace marks, but main still prints 70. Rewrite it so that main gets 75, without using arrays.

Input
marks = 70
Expected output
Marks with grace: 75
Show solution and explanation
public class Main {
    static int addGrace(int marks) {
        return marks + 5;
    }

    public static void main(String[] args) {
        int marks = 70;
        marks = addGrace(marks);
        System.out.println("Marks with grace: " + marks);
    }
}
Marks with grace: 75

How it works: a primitive argument is copied into the parameter, so changing the parameter can never reach main. The fix is to return the new value and let main assign it: marks = addGrace(marks);.

P2Easy

Add 10 to every element

Write void addToAll(int[] a, int amount) that increases every element of the caller's array.

Input
scores = {40, 55, 72}, amount = 10
Expected output
[50, 65, 82]
Show solution and explanation
import java.util.Arrays;

public class Main {
    static void addToAll(int[] a, int amount) {
        for (int i = 0; i < a.length; i++) {
            a[i] += amount;
        }
    }

    public static void main(String[] args) {
        int[] scores = {40, 55, 72};
        addToAll(scores, 10);
        System.out.println(Arrays.toString(scores));
    }
}
[50, 65, 82]

How it works: a receives a copy of the reference, so it points to the same array as scores. Writing a[i] changes that shared array, and main sees it. No return value is needed.

P3Easy

Swap two elements

A method can't swap two int variables of its caller, but it can swap two elements of an array. Write swapItems(int[] a, int i, int j).

Input
queue = {101, 102, 103, 104}, swap positions 0 and 3
Expected output
[104, 102, 103, 101]
Show solution and explanation
import java.util.Arrays;

public class Main {
    static void swapItems(int[] a, int i, int j) {
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }

    public static void main(String[] args) {
        int[] queue = {101, 102, 103, 104};
        swapItems(queue, 0, 3);
        System.out.println(Arrays.toString(queue));
    }
}
[104, 102, 103, 101]

How it works: the elements live inside one array object on the heap. The method changes that object through its copy of the reference, so the swap is visible in main. This is exactly how sorting methods like Arrays.sort rearrange your array.

P4Easy

Add a country code to a phone number

Write a method that adds +91 in front of a phone number. Remember that a String can't be changed, so return the new String.

Input
phone = "9876543210"
Expected output
9876543210
+91 9876543210
Show solution and explanation
public class Main {
    static String withCountryCode(String phone) {
        return "+91 " + phone;
    }

    public static void main(String[] args) {
        String phone = "9876543210";
        String full = withCountryCode(phone);
        System.out.println(phone);
        System.out.println(full);
    }
}
9876543210 +91 9876543210

How it works: Strings are immutable (Lesson 4.3), so a method can't change the caller's String even though it receives a reference to it. It builds a new String and returns it. The original phone is untouched.

P5Medium

Reverse an array in place

Write void reverse(int[] a) that reverses the caller's array itself, without creating a second array.

Input
a = {1, 2, 3, 4, 5}
Expected output
[5, 4, 3, 2, 1]
Show solution and explanation
import java.util.Arrays;

public class Main {
    static void reverse(int[] a) {
        for (int i = 0, j = a.length - 1; i < j; i++, j--) {
            int temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }
    }

    public static void main(String[] args) {
        int[] a = {1, 2, 3, 4, 5};
        reverse(a);
        System.out.println(Arrays.toString(a));
    }
}
[5, 4, 3, 2, 1]

How it works: two indexes walk inward, swapping as they go. Because the method only mutates the shared array (it never assigns a = …), the caller sees the reversed order.

P6Medium

Adding an item needs a returned array

Arrays can't grow. Write int[] append(int[] a, int value) that returns a new array one element longer. Show why assigning inside a void method wouldn't work.

Input
ids = {7, 8}, append 9
Expected output
After appendWrong: [7, 8]
After append:      [7, 8, 9]
Show solution and explanation
import java.util.Arrays;

public class Main {
    static void appendWrong(int[] a, int value) {
        a = Arrays.copyOf(a, a.length + 1);
        a[a.length - 1] = value;
    }

    static int[] append(int[] a, int value) {
        int[] bigger = Arrays.copyOf(a, a.length + 1);
        bigger[bigger.length - 1] = value;
        return bigger;
    }

    public static void main(String[] args) {
        int[] ids = {7, 8};
        appendWrong(ids, 9);
        System.out.println("After appendWrong: " + Arrays.toString(ids));
        ids = append(ids, 9);
        System.out.println("After append:      " + Arrays.toString(ids));
    }
}
After appendWrong: [7, 8] After append: [7, 8, 9]

How it works: appendWrong builds the bigger array but only points its own copy of the reference at it; main's ids still refers to the old array. append returns the new array, and main reassigns ids. This is the pattern behind every "grow" operation.

P7Medium

Scale marks out of 100

Test marks were out of 40. Write void scaleTo100(double[] marks, double outOf) that converts every mark in place.

Input
marks = {30, 36, 22}, out of 40
Expected output
[75.0, 90.0, 55.0]
Show solution and explanation
import java.util.Arrays;

public class Main {
    static void scaleTo100(double[] marks, double outOf) {
        for (int i = 0; i < marks.length; i++) {
            marks[i] = marks[i] * 100 / outOf;
        }
    }

    public static void main(String[] args) {
        double[] marks = {30, 36, 22};
        scaleTo100(marks, 40);
        System.out.println(Arrays.toString(marks));
    }
}
[75.0, 90.0, 55.0]

How it works: the array is changed in place. The parameter outOf is a primitive copy, so the method could even change it without affecting main. Mutating the caller's data is powerful but surprising, so the method name should make it clear.

P8Medium

Build text through a StringBuilder parameter

Write void addLine(StringBuilder sb, String text) that appends a numbered line. Compare it with a String parameter, which can't be changed.

Input
lines: "Rice", "Dal"
Expected output
SHOPPING
- Rice
- Dal
SHOPPING
Show solution and explanation
public class Main {
    static void addLine(StringBuilder sb, String text) {
        sb.append("- ").append(text).append('\n');
    }

    static void addLineToString(String s, String text) {
        s = s + "- " + text + "\n";
    }

    public static void main(String[] args) {
        StringBuilder list = new StringBuilder("SHOPPING\n");
        addLine(list, "Rice");
        addLine(list, "Dal");
        System.out.print(list);

        String plain = "SHOPPING\n";
        addLineToString(plain, "Rice");
        System.out.print(plain);
    }
}
SHOPPING - Rice - Dal SHOPPING

How it works: both methods receive a copy of a reference. append changes the StringBuilder object itself, which main also refers to. With a String, s + … creates a new object and only the method's local s points to it, so main still prints just the heading.

P9Business problem

Apply GST to a cart in place

A cart holds item prices before tax. Write void addGst(double[] prices, double percent) that converts every price to its tax-inclusive amount, then print the cart and its total.

Input
prices = {1000, 250, 80}, GST = 18%
Expected output
With GST: [1180.0, 295.0, 94.4]
Total: Rs 1569.40
Show solution and explanation
import java.util.Arrays;

public class Main {
    static void addGst(double[] prices, double percent) {
        for (int i = 0; i < prices.length; i++) {
            prices[i] = prices[i] + prices[i] * percent / 100;
        }
    }

    static double total(double[] prices) {
        double sum = 0;
        for (double p : prices) {
            sum += p;
        }
        return sum;
    }

    public static void main(String[] args) {
        double[] cart = {1000, 250, 80};
        addGst(cart, 18);
        System.out.println("With GST: " + Arrays.toString(cart));
        System.out.printf("Total: Rs %.2f%n", total(cart));
    }
}
With GST: [1180.0, 295.0, 94.4] Total: Rs 1569.40

How it works:

  1. addGst mutates the shared array, so it needs no return value.
  2. total only reads the array, and returns a number.
  3. Calling addGst twice would add tax twice, because the first call already changed the data. In-place updates must be done exactly once.
P10Business problem

Deposit into an account by index

Account balances are kept in an array. Write boolean deposit(long[] balances, int account, long amount) that adds the amount and returns true, or returns false (changing nothing) for an invalid account number or amount.

Input
balances = {15000, 2300, 98000}
deposit 5000 into account 1, then -10 into account 2, then 100 into account 7
Expected output
true
false
false
[15000, 7300, 98000]
Show solution and explanation
import java.util.Arrays;

public class Main {
    static boolean deposit(long[] balances, int account, long amount) {
        if (account < 0 || account >= balances.length) {
            return false;
        }
        if (amount <= 0) {
            return false;
        }
        balances[account] += amount;
        return true;
    }

    public static void main(String[] args) {
        long[] balances = {15000, 2300, 98000};
        System.out.println(deposit(balances, 1, 5000));
        System.out.println(deposit(balances, 2, -10));
        System.out.println(deposit(balances, 7, 100));
        System.out.println(Arrays.toString(balances));
    }
}
true false false [15000, 7300, 98000]

How it works:

  1. The method both changes the shared array and returns a success flag, two separate channels back to the caller.
  2. Guard clauses run before any change, so a failed deposit leaves the data exactly as it was.
  3. Only account 1 changed: 2300 + 5000 = 7300.
P11Business problem

Price revision without touching the original list

A shop wants to preview a 7% price increase without changing the current price list. Write int[] revised(int[] prices, int percent) that returns a new array and leaves the original untouched.

Input
prices = {120, 450, 999}, increase = 7%
Expected output
Current: [120, 450, 999]
Preview: [128, 482, 1069]
Show solution and explanation
import java.util.Arrays;

public class Main {
    static int[] revised(int[] prices, int percent) {
        int[] result = new int[prices.length];
        for (int i = 0; i < prices.length; i++) {
            result[i] = (int) Math.round(prices[i] * (100 + percent) / 100.0);
        }
        return result;
    }

    public static void main(String[] args) {
        int[] current = {120, 450, 999};
        int[] preview = revised(current, 7);
        System.out.println("Current: " + Arrays.toString(current));
        System.out.println("Preview: " + Arrays.toString(preview));
    }
}
Current: [120, 450, 999] Preview: [128, 482, 1069]

How it works:

  1. Because the method could change the caller's array, choosing not to is a design decision: it writes into a fresh array instead.
  2. The caller keeps both versions, so the manager can compare before approving.
  3. Math.round returns a long, so it is cast to int: 999 × 1.07 = 1068.93 rounds to 1069.

Real-world scenario questions (asked in company interviews)

Pass-by-value bugs show up as data that changes when it shouldn't, or doesn't change when it should. Decide your answer first, then open the model answer.

Banking · statementsFresher

The monthly charge that never got applied

A method void applyCharge(double balance, double charge) { balance = balance - charge; } is called for every account at month end. Testers report that balances never go down. Explain the bug and fix it.

The interviewer is checking: do you know that primitive parameters are copies?

Show a strong answer
  • balance is a copy of the caller's double. Subtracting changes only the copy, which disappears when the method returns.
  • Fix: return the new value, static double afterCharge(double balance, double charge), and assign it: balance = afterCharge(balance, charge);.
  • In a real system the balance belongs to an account object or a database row, and the method would update that (Module 6).
  • Add a test that checks the balance after the call, not just that the method runs.

Avoid: "Java passes by reference, so it should have worked." That's the misconception causing the bug.

E-commerce · reports1–3 years

The report that changed the live prices

A "festival preview" report calls applyDiscount(prices, 20) to show what prices would look like after a 20% discount. After the report runs, the live website shows discounted prices too. Why, and how should it be designed?

The interviewer is checking: do you understand shared references and when a method should not mutate its input?

Show a strong answer
  • The method received a copy of the reference to the live price array and mutated it. The report and the website share one array, so both changed.
  • For a preview, use a method without side effects: int[] discounted(int[] prices, int pct) that writes into a new array and returns it.
  • Or copy first: applyDiscount(Arrays.copyOf(prices, prices.length), 20).
  • Name in-place methods clearly (e.g. applyDiscountInPlace) so callers know they mutate.

Avoid: fixing it by "resetting" prices after the report. Another request could read them in between.

Logistics · data loadingFresher

NullPointerException after "loading" the data

Code declares int[] routes = null;, calls loadRoutes(routes); (which does routes = new int[]{…}; inside), and then crashes with a NullPointerException on routes[0]. The developer insists the method creates the array. What's happening?

The interviewer is checking: can you spot a reassigned parameter that the caller never sees?

Show a strong answer
  • The method does create an array, but assigns it to its own parameter, a copy of the caller's null reference. The caller's routes is still null.
  • Fix: make the method return the array, static int[] loadRoutes(), and write routes = loadRoutes();.
  • The stack trace points to the line in the caller, which is a hint that the caller's variable is the null one.

Avoid: wrapping the access in a null check. The data still never arrives.

Fintech · API design3+ years

Should calculation methods modify their inputs?

Your team is designing a library of loan calculations (EMI schedules, prepayment effects) used by many services. One developer proposes methods that take a schedule array and modify it in place "for performance". What guidelines would you set?

The interviewer is checking: can you reason about side effects, shared references and API safety?

Show a strong answer
  • Default to methods without side effects: take inputs, return a new result. Callers can't be surprised, results are easy to test, and the same input can be reused safely, even from several threads.
  • The cost of creating a new array of a few hundred instalments is tiny compared with the cost of a wrong loan schedule. Measure before optimising.
  • If an in-place version is truly needed, make it a separate, clearly named method and document the mutation.
  • Prefer immutable result types (records, unmodifiable lists) so later code can't change them either.

Avoid: mutating inputs by default. Shared references turn one service's change into another service's bug.