Recursion
What Is Recursion?
Recursion is when a method calls itself to solve a smaller version of the same problem. Every recursive method needs two essential parts:
Base case — the simplest form of the problem, solved directly without another recursive call. This is what stops the recursion.
Recursive case — the method calls itself with a smaller or simpler input, moving closer to the base case each time.
Classic Example: Factorial
The factorial of n (written n!) is the product of all positive integers up to n. It has a natural recursive definition: 0! is 1 (the base case), and n! is n times (n - 1)! for any n greater than 0.
Factorial.java
public class Factorial {
static long factorial(int n) {
if (n == 0) { // base case
return 1;
}
return n * factorial(n - 1); // recursive case
}
public static void main(String[] args) {
System.out.println(factorial(5)); // 5 * 4 * 3 * 2 * 1
System.out.println(factorial(0));
}
}120 1
Classic Example: Fibonacci
The Fibonacci sequence (0, 1, 1, 2, 3, 5, 8, 13, ...) is defined so that each number is the sum of the two before it. It has two base cases (fib(0) and fib(1)) and a recursive case that calls itself twice:
Fibonacci.java
public class Fibonacci {
static int fib(int n) {
if (n == 0 || n == 1) { // base cases
return n;
}
return fib(n - 1) + fib(n - 2); // recursive case
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
System.out.print(fib(i) + " ");
}
}
}0 1 1 2 3 5 8 13 21 34
StackOverflowError
Each recursive call adds a new frame to the call stack to track its local variables and where to resume execution. If recursion goes too deep — because there is no base case, the base case is never reached, or the input is simply too large — the JVM runs out of stack space and throws a StackOverflowError.
A recursive call with no base case
static void runaway(int n) {
System.out.println(n);
runaway(n + 1); // never stops — no base case
}No Tail-Call Optimization in Java
When to Prefer Iteration
Because Java has no tail-call optimization, recursion carries a real cost: stack frame overhead per call and a hard ceiling on recursion depth (typically a few thousand to tens of thousands of frames, depending on stack size and frame complexity). For performance- or depth-sensitive code, an equivalent iterative loop is usually safer and faster in Java.
The same factorial, written iteratively
static long factorialIterative(int n) {
long result = 1;
for (int i = 2; i <= n; i++) {
result *= i;
}
return result;
}Recursion tends to read more naturally for problems that are inherently recursive: tree/graph traversal, divide-and-conquer algorithms, backtracking
Iteration is usually preferable when the recursion depth could scale with input size and a loop expresses the same logic just as clearly
When you do need deep recursion, consider increasing the JVM stack size (the -Xss flag) or restructuring the algorithm rather than relying on default limits