Methods · Lesson 19 of 42
Pass-by-Value and Recursion
Two ideas complete your understanding of methods. First, how arguments are passed – "Is Java pass-by-value or pass-by-reference?" is asked in almost every Java interview, and most candidates get it slightly wrong. Second, recursion: a method solving a problem by calling itself on a smaller version of it.
In this lesson
- Java is always pass-by-value – and why objects seem different
- Changing an object vs reassigning a parameter
- Recursion: base case, recursive case, call stack
- When recursion is a good idea – and when it isn't
Java is always pass-by-value
When you call a method, Java copies the value of each argument into the parameter. For primitives the value is the number itself, so the method can't affect the caller's variable:
public class Main {
static void addBonus(int marks) {
marks += 10; // changes the copy only
System.out.println("inside: " + marks);
}
public static void main(String[] args) {
int marks = 70;
addBonus(marks);
System.out.println("outside: " + marks);
}
}
inside: 80 outside: 70
Objects: the reference is copied
For objects and arrays, the variable holds a reference. Java copies that reference – so the method gets a second reference to the same object. It can change the object's contents, but reassigning the parameter only changes its own copy.
import java.util.Arrays;
public class Main {
static void addBonus(int[] marks) {
marks[0] += 10; // changes the shared array
}
static void replace(int[] marks) {
marks = new int[]{0, 0, 0}; // reassigns the local copy only
}
public static void main(String[] args) {
int[] marks = {70, 80, 90};
addBonus(marks);
System.out.println(Arrays.toString(marks));
replace(marks);
System.out.println(Arrays.toString(marks));
}
}
[80, 80, 90] [80, 80, 90]
What gets copied
- main: marks → array #1the caller's reference
- call addBonus(marks)the reference is copied into the parameter
- both point to array #1marks[0] += 10 is visible to main
- replace: marks = new arrayonly the copy moves; main still sees array #1
The interview answer
"Java is strictly pass-by-value. For objects, the value passed is a copy of the reference, so a method can modify the object it points to but cannot make the caller's variable point to a different object." The classic proof: a swap(a, b) method on two object references can never swap the caller's variables.
Recursion
A recursive method calls itself. Every correct recursive method has two parts:
- a base case that is answered directly and stops the recursion;
- a recursive case that calls the method on a smaller input, moving towards the base case.
public class Main {
static long factorial(int n) {
if (n <= 1) return 1; // base case
return n * factorial(n - 1); // recursive case
}
public static void main(String[] args) {
System.out.println(factorial(5));
System.out.println(factorial(20));
}
}
120 2432902008176640000
factorial(4) on the call stack
- factorial(4)waits for 4 × factorial(3)
- factorial(3)waits for 3 × factorial(2)
- factorial(2)waits for 2 × factorial(1)
- factorial(1) = 1base case – no more calls
- Unwinding2 × 1 = 2 → 3 × 2 = 6 → 4 × 6 = 24
More recursive classics
public class Main {
static int sumDigits(int n) {
return n < 10 ? n : n % 10 + sumDigits(n / 10);
}
static boolean isPalindrome(String s, int i, int j) {
if (i >= j) return true;
return s.charAt(i) == s.charAt(j) && isPalindrome(s, i + 1, j - 1);
}
static long power(long base, int exp) { // fast exponentiation
if (exp == 0) return 1;
long half = power(base, exp / 2);
return exp % 2 == 0 ? half * half : half * half * base;
}
static int binarySearch(int[] a, int key, int lo, int hi) {
if (lo > hi) return -1;
int mid = (lo + hi) >>> 1;
if (a[mid] == key) return mid;
return a[mid] < key ? binarySearch(a, key, mid + 1, hi) : binarySearch(a, key, lo, mid - 1);
}
public static void main(String[] args) {
System.out.println(sumDigits(4096));
System.out.println(isPalindrome("racecar", 0, 6));
System.out.println(power(2, 40));
System.out.println(binarySearch(new int[]{3, 8, 15, 23, 42, 57}, 42, 0, 5));
}
}
19 true 1099511627776 4
The cost of recursion
Every call adds a stack frame. Without a base case – or with very deep input – the stack runs out:
public class Main {
static int depth = 0;
static void dive() {
depth++;
dive(); // no base case
}
public static void main(String[] args) {
try {
dive();
} catch (StackOverflowError e) {
System.out.println("StackOverflowError after many calls: " + (depth > 1000));
}
}
}
StackOverflowError after many calls: true
Recursion can also repeat work. Naive Fibonacci recalculates the same values exponentially many times; storing results (memoisation) or using a loop fixes it:
public class Main {
static long[] memo = new long[91];
static long fib(int n) {
if (n < 2) return n;
if (memo[n] != 0) return memo[n];
return memo[n] = fib(n - 1) + fib(n - 2);
}
public static void main(String[] args) {
System.out.println(fib(10) + " " + fib(50) + " " + fib(90));
}
}
55 12586269025 2880067194370816120
| Use recursion for | Prefer a loop for |
|---|---|
| Trees, folders, nested data, divide-and-conquer (merge sort, quick sort), backtracking | Simple counting, sums, linear scans – anything that may go thousands of levels deep |
Practice
Try it yourself
Write a recursive method that reverses a string.
Show solution
public class Main { static String reverse(String s) { if (s.length() <= 1) return s; return reverse(s.substring(1)) + s.charAt(0); } public static void main(String[] args) { System.out.println(reverse("LearnFrenzy")); } }yznerFnraeL
Recursively compute the GCD of 84 and 36 (Euclid: gcd(a, b) = gcd(b, a % b)).
Show solution
public class Main { static int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); } public static void main(String[] args) { System.out.println(gcd(84, 36)); } }12
Quick check
Is Java pass-by-reference for objects?
No. It is always pass-by-value; for objects the value is a copy of the reference.
Can a method change the caller's array elements?
Yes – it receives a reference to the same array.
What happens if a recursive method has no base case?
It keeps calling itself until the stack is exhausted and Java throws StackOverflowError.
Summary
- Java copies argument values; for objects it copies the reference, so contents can change but the caller's variable can't be reassigned.
- Recursion needs a base case and must move towards it; every call uses a stack frame.
- Memoise repeated work; prefer loops when depth could be large.
Interview practice: Core Java interview questions.
Finished this lesson?
Mark it complete to track your progress, then carry on with the next lesson.