Skip to content
Laviyx Learn

Try

Java course menu

Java Parameters and Return Values: Pass by Value Explained

Understand how Java passes arguments: always by value, what that means for primitives versus object references, why a method can change an object's contents but not the caller's variable, varargs, and returning several values with a record.

Beginner11 min readUpdated
On this page

"Is Java pass by value or pass by reference?" is one of the most common Java interview questions. The precise answer: Java is always pass by value. But for objects, the value that is copied is a reference, which explains behaviour that surprises many beginners.

Primitives: the method gets a copy#

Java
public class Main {
    static void addBonus(int salary) {
        salary += 5000;
        System.out.println("Inside: " + salary);
    }

    public static void main(String[] args) {
        int salary = 50000;
        addBonus(salary);
        System.out.println("Outside: " + salary);
    }
}
Output
Inside: 55000
Outside: 50000

The parameter is a separate variable holding a copy of the value. Changing it has no effect on the caller. To get a changed value back, return it:

Java
public class Main {
    static int withBonus(int salary) {
        return salary + 5000;
    }

    public static void main(String[] args) {
        int salary = 50000;
        salary = withBonus(salary);
        System.out.println(salary);
    }
}
Output
55000

Objects: a copy of the reference#

A variable of an object type (an array, a list, your own class) holds a reference to the object. Passing it copies the reference, so the method and the caller refer to the same object. The method can therefore change the object's contents:

Java
import java.util.ArrayList;
import java.util.List;

public class Main {
    static void addItem(List<String> cart, String item) {
        cart.add(item);
    }

    static void doubleAll(int[] values) {
        for (int i = 0; i < values.length; i++) {
            values[i] *= 2;
        }
    }

    public static void main(String[] args) {
        List<String> cart = new ArrayList<>();
        addItem(cart, "Milk");
        addItem(cart, "Bread");
        System.out.println(cart);

        int[] numbers = {1, 2, 3};
        doubleAll(numbers);
        System.out.println(numbers[0] + " " + numbers[1] + " " + numbers[2]);
    }
}
Output
[Milk, Bread]
2 4 6

But a method cannot replace the caller's object#

Assigning a new object to the parameter only changes the method's copy of the reference. The caller's variable still points to the original:

Java
import java.util.ArrayList;
import java.util.List;

public class Main {
    static void replace(List<String> cart) {
        cart = new ArrayList<>();   // only the local copy now points to a new list
        cart.add("Something new");
    }

    public static void main(String[] args) {
        List<String> cart = new ArrayList<>(List.of("Milk"));
        replace(cart);
        System.out.println(cart);
    }
}
Output
[Milk]

That is why a classic swap(a, b) method cannot work in Java: the method can only swap its own copies.

Inside the method you…Effect on the caller
change a primitive parameternone
call a method that modifies the object (list.add, arr[i] = …)visible: same object
assign a new object to the parameternone

Strings are immutable#

String methods never change the string; they return a new one. So a method cannot change a caller's string through its parameter, and must return the result:

Java
public class Main {
    static String shout(String text) {
        return text.toUpperCase() + "!";
    }

    public static void main(String[] args) {
        String message = "hello";
        String loud = shout(message);
        System.out.println(message + " / " + loud);
    }
}
Output
hello / HELLO!

Varargs: any number of arguments#

Type... name lets a method accept any number of arguments, which arrive as an array:

Java
public class Main {
    static double average(double... numbers) {
        if (numbers.length == 0) return 0;
        double sum = 0;
        for (double n : numbers) sum += n;
        return sum / numbers.length;
    }

    public static void main(String[] args) {
        System.out.println(average(4, 8, 6));
        System.out.println(average(10));
        System.out.println(average());
    }
}
Output
6.0
10.0
0.0

The varargs parameter must be the last parameter. String.format and List.of use varargs.

Returning several values#

A method returns one value, but that value can be an object holding several parts. A record (Java 16+) is the neatest way to define one; see Records and Enums:

Java
public class Main {
    record MinMax(int min, int max) {}

    static MinMax minMax(int[] values) {
        int lo = values[0];
        int hi = values[0];
        for (int v : values) {
            lo = Math.min(lo, v);
            hi = Math.max(hi, v);
        }
        return new MinMax(lo, hi);
    }

    public static void main(String[] args) {
        MinMax result = minMax(new int[] {12, 7, 21, 9});
        System.out.println(result.min() + " " + result.max());
        System.out.println(result);
    }
}
Output
7 21
MinMax[min=7, max=21]

Common mistakes#

Practice#

Practice exercise

Write void applyDiscount(double[] prices, double percent) that reduces every price in the array by the given percentage, and test it with {850, 1200, 99.5} and 10%.

Show solution
Java
import java.util.Arrays;

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

    public static void main(String[] args) {
        double[] prices = {850, 1200, 99.5};
        applyDiscount(prices, 10);
        System.out.println(Arrays.toString(prices));
    }
}
Output
[765.0, 1080.0, 89.55]

The method changes the caller's array because both refer to the same array object. Arrays.toString formats an array for printing.

Key takeaway#

Finished this lesson?