5.3 Parameters, Return Values, and Pass-by-Value
Prerequisites: Ability to declare and call methods Estimated time: 35 minutes You are ready to continue when: You can demonstrate with primitive and reference types that Java only passes values
Parameters Receive Copies of Argument Values
static void buildItem(String name, int days) {
System.out.println(name + ": " + days + " days");
}
buildItem("standard item", 3);name and days are parameters. The string and integer expressions are arguments. Java evaluates them from left to right, copying the result value into the parameter variables of the new method call.
Passing Primitive Values
static void increase(int value) {
value++;
System.out.println("inside=" + value);
}
int count = 10;
increase(count);
System.out.println("outside=" + count);Output:
inside=11
outside=10The parameter value receives a copy of the integer 10. Modifying it does not change the caller's variable count.
Reference Types Still Pass by Value
static void changeFirst(int[] values) {
values[0] = 99;
}
int[] numbers = {1, 2, 3};
changeFirst(numbers);
System.out.println(numbers[0]); // 99The copied value is a reference. Both the caller's variable and the parameter's copy can identify the same array object, so changes made through either reference are observable by the other.
This is not equivalent to "passing by reference." Reassigning the parameter does not alter the caller's variable:
static void replace(int[] values) {
values = new int[] {7, 8, 9};
values[0] = 100;
}
int[] numbers = {1, 2, 3};
replace(numbers);
System.out.println(numbers[0]); // 1Do not directly refer to the reference value as a visible memory address. The Java language only guarantees that it can identify an object or be null. Ordinary code cannot perform address arithmetic on references.
final Parameters Are Not Equal to Immutable Objects
static void update(final int[] values) {
values[0] = 42; // Legal: Modify object
// values = new int[1]; // Compilation error: Cannot reassign parameter
}final prevents reassignment of the parameter; it does not freeze the array contents.
Return a Result, Not Try to Modify the Caller's Local Variables
This method cannot swap the caller’s local variables:
static void swap(int left, int right) {
int temporary = left;
left = right;
right = temporary;
}Calling swap(a, b) does not swap the two local variables of the caller. A clearer design is to return a result object or array; for beginners, it's acceptable to return a two-element array:
static int[] swapped(int left, int right) {
return new int[] {right, left};
}
int[] result = swapped(3, 5);After learning about record, you can return a result type with field names, avoiding the ambiguity at the result[0] location.
Return Values and Failure Contracts
static int divide(int dividend, int divisor) {
if (divisor == 0) {
throw new IllegalArgumentException("divisor is zero");
}
if (dividend == Integer.MIN_VALUE && divisor == -1) {
throw new ArithmeticException("division overflows int");
}
return dividend / divisor;
}A method may return normally, throw an exception, or abruptly terminate due to a more severe error. This example rejects zero divisors and MIN_VALUE / -1; other inputs use Java integer division, truncating toward zero. Documentation should specify range, units, and failure behavior as well as the return type.
Multiple Parameters and Parameter Objects
static int quote(String material, int kilograms,
boolean rush, int customerLevel) {
// ...
}Multiple parameters do not automatically equate to a design flaw, but when similar types, boolean switches, or frequently grouped changes appear in sequence, calling them can lead to easy mistakes: quote("iron", 5, true, 2) is difficult to self-explain. Introducing a named request object or splitting responsibilities can help.
Variable-Argument Parameters as Array Syntactic Sugar
static int sum(int... values) {
int total = 0;
for (int value : values) {
total = Math.addExact(total, value);
}
return total;
}
System.out.println(sum(1, 2, 3));The method body treats values as an array. The caller can also pass int[]. sum() receives an empty array and returns 0; sum((int[]) null) receives null and throws NullPointerException during iteration. No arguments and a null array are different inputs. A method can have at most one variable-argument parameter, and it must be placed at the end of the parameter list. Overload resolution prefers the applicable fixed-parameter form before the variable-argument phase. When ambiguity arises from mixing both, testing is required.
Python and C++ Boundaries
Python also binds object references to new parameter names; mutable objects can be observed by multiple parties, and re-binding parameters does not alter the caller's name. The term is often described as call by sharing, and this cannot be used to label Java as reference passing.
C++ explicitly distinguishes between value, reference, and pointer parameters. Java does not have a parameter syntax that allows for aliasing of the caller's local variables.
Professional Reference
| Phenomenon | Cause |
|---|---|
| Modifying the basic formal parameter, the caller remains unchanged | A copy of the basic value was made |
| Modifying array elements, the caller can see the change | Two reference values identify the same mutable object |
| Reassigning the array formal parameter, the caller remains unchanged | Only the formal parameter copy was changed |
final The array can still be modified | The final constraint on the reference variable does not freeze the object |
| Want to return multiple results | Return a result type with a clear structure |
Before Moving On
Use the three examples increase, changeFirst, and replace to answer the same question: what is actually copied when a call is made? Changes to array elements are visible from shared objects, and reassignment of parameters does not affect the caller's variable. final also only constrains the parameter itself. Return values must also clearly specify the range, units, and failure modes.
Each call enters a new execution context.
Specification: JLS §15.12.4.2: Argument Evaluation.