Methods & Recursion
A method takes parameters, does its job, and (optionally) returns a value — saving you from repeating code.
Java follows "pass by value" — a method gets a copy of the values, the original variable never changes (for objects, it gets a copy of the reference).
In recursion, a method calls itself — like factorial(5) = 5 * factorial(4)... until the base case (factorial(1)=1) is reached.
int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}- Method = a reusable code block
- Pass by value — you get a copy
- Recursion = a method calling itself
A method's signature is its name + parameter types/order (the return type is NOT part of the signature — which is why two methods can't differ by return type alone, they must differ by parameters to be overloaded).
void means the method returns nothing. If a method returns something (like int, String, or an object), a return statement is required on every possible code path — the compiler checks that no path can end the method without a return, otherwise you get a "missing return statement" error.
... (three dots) lets a method take a variable number of arguments — internally it's treated like an array inside the method. The caller can flexibly pass 0, 1, or as many arguments as they like.
A method can have only one varargs parameter, and it must always be the *last* parameter (if there are other regular parameters too). Built-in methods like System.out.printf() and String.format() use varargs themselves.
int sum(int... nums) {
int total = 0;
for (int n : nums) total += n;
return total;
}
sum(1, 2, 3); // works
sum(1, 2, 3, 4, 5); // works bhi
sum(); // works, total = 0Java is always pass-by-value, whether it's a primitive or an object — this is a very common interview trick question. For primitives, you get a copy of the value (the original never changes, no matter what you do inside the method).
For objects, you get a copy of the "reference" (the heap address) — meaning both (the original variable and the parameter) point to the same object. So you can change fields inside the object (visible outside too), but if you reassign the parameter to a new object (s = new Student()), only the local copy is updated — the original reference doesn't change.
void modify(Student s) {
s.name = "Changed"; // ye dikhega original object mein (same reference)
s = new Student(); // ye NAHI dikhega — sirf local copy reassign hui
}Factorial and Fibonacci are classic recursion examples. Every recursive method needs two things: a base case (where recursion stops) and a recursive case (which shrinks the problem and calls itself).
Without a base case, a method will keep calling itself infinitely, until a StackOverflowError occurs — every recursive call takes up a "stack frame", and the JVM's call stack has a fixed size.
The naive recursive version of Fibonacci (as below) is very inefficient for large n — because the same values get calculated over and over (fibonacci(5) calculates fibonacci(3) twice). Ways to fix this include "memoization" (caching results) or an iterative approach.
int factorial(int n) {
if (n <= 1) return 1; // base case
return n * factorial(n - 1); // recursive case
}
int fibonacci(int n) {
if (n <= 1) return n;
return fibonacci(n-1) + fibonacci(n-2);
}