Advanced Methods
This lesson dives deeper into advanced method features, including recursion, static methods, and method references.
Recursion
Recursion is a programming technique where a method calls itself.
Three Elements of Recursion
| Element | Description |
|---|---|
| Base case | The condition that stops recursion |
| Recursive call | The method calls itself |
| Problem reduction | Each recursive call reduces the problem size |
Example: Calculate Factorial
JAVA
public class Factorial {
public static long factorial(int n) {
// Base case
if (n <= 1) {
return 1;
}
// Recursive call
return n * factorial(n - 1);
}
public static void main(String[] args) {
System.out.println("5! = " + factorial(5)); // 120
System.out.println("10! = " + factorial(10)); // 3628800
}
}
Recursion Process
TEXT
factorial(5)
= 5 * factorial(4)
= 5 * 4 * factorial(3)
= 5 * 4 * 3 * factorial(2)
= 5 * 4 * 3 * 2 * factorial(1)
= 5 * 4 * 3 * 2 * 1
= 120
Example: Fibonacci Sequence
JAVA
public class Fibonacci {
public static int fibonacci(int n) {
if (n <= 1) {
return n;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
System.out.print(fibonacci(i) + " ");
}
// Output: 0 1 1 2 3 5 8 13 21 34
}
}
⚠️ Note: Simple recursion is inefficient. Use memoization to optimize. For Fibonacci, iteration is more efficient.
Example: Recursive Directory Traversal
JAVA
import java.io.File;
public class ListFiles {
public static void listFiles(File dir, String indent) {
File[] files = dir.listFiles();
if (files == null) return;
for (File file : files) {
System.out.println(indent + file.getName());
if (file.isDirectory()) {
listFiles(file, indent + " ");
}
}
}
public static void main(String[] args) {
File dir = new File(".");
listFiles(dir, "");
}
}
Static vs Instance Methods
Static Methods
Methods modified with static belong to the class and can be called without creating an object.
JAVA
public class MathUtils {
public static int add(int a, int b) {
return a + b;
}
}
// Call
int sum = MathUtils.add(3, 5);
Instance Methods
Methods without static belong to objects and require creating an object to call.
JAVA
public class Calculator {
public int add(int a, int b) {
return a + b;
}
}
// Call
Calculator calc = new Calculator();
int sum = calc.add(3, 5);
Comparison
| Feature | Static Method | Instance Method |
|---|---|---|
| Keyword | static |
None |
| Calling style | ClassName.method() |
object.method() |
| Member access | Can only access static members | Can access all members |
| this keyword | Cannot use | Can use |
Example: Static vs Instance Methods
JAVA
public class MethodTypeDemo {
private int count = 0;
// Static method
public static int add(int a, int b) {
return a + b;
}
// Instance method
public void increment() {
count++;
}
public int getCount() {
return count;
}
public static void main(String[] args) {
// Static method called directly
System.out.println(add(3, 5)); // 8
// Instance method requires object
MethodTypeDemo demo = new MethodTypeDemo();
demo.increment();
demo.increment();
System.out.println(demo.getCount()); // 2
}
}
Utility Class Design
Utility classes typically only contain static methods and don't require creating objects.
Example: String Utility Class
JAVA
public class StringUtils {
// Check if empty
public static boolean isEmpty(String str) {
return str == null || str.isEmpty();
}
// Check if blank
public static boolean isBlank(String str) {
return str == null || str.trim().isEmpty();
}
// Reverse string
public static String reverse(String str) {
if (str == null) return null;
return new StringBuilder(str).reverse().toString();
}
// Capitalize first letter
public static String capitalize(String str) {
if (isEmpty(str)) return str;
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
public static void main(String[] args) {
System.out.println(isEmpty("")); // true
System.out.println(isEmpty("hello")); // false
System.out.println(reverse("hello")); // olleh
System.out.println(capitalize("hello")); // Hello
}
}
Method References
Java 8 introduced method references as a shorthand for Lambda expressions.
Four Types of Method References
| Type | Syntax | Example |
|---|---|---|
| Static method | ClassName::staticMethod |
Math::abs |
| Instance method | object::instanceMethod |
System.out::println |
| Instance method of a class | ClassName::instanceMethod |
String::length |
| Constructor | ClassName::new |
ArrayList::new |
Example: Method References
JAVA
import java.util.Arrays;
import java.util.List;
public class MethodRefDemo {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// Lambda expression
names.forEach(name -> System.out.println(name));
// Method reference (more concise)
names.forEach(System.out::println);
// Static method reference
List<Integer> numbers = Arrays.asList(-3, -1, 0, 2, 5);
numbers.stream()
.map(Math::abs)
.forEach(System.out::println);
// Output: 3 1 0 2 5
}
}
Example: Constructor Reference
JAVA
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
public class ConstructorRefDemo {
public static void main(String[] args) {
// Lambda expression
Supplier<List<String>> listFactory = () -> new ArrayList<>();
// Constructor reference
Supplier<List<String>> listFactory2 = ArrayList::new;
List<String> list = listFactory2.get();
list.add("Hello");
System.out.println(list); // [Hello]
}
}
Recursion Optimization
Tail Recursion
Tail recursion is when the recursive call is the last operation in the function.
JAVA
// Regular recursion (not tail recursion)
public static long factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1); // Multiply n after recursion
}
// Tail recursion version
public static long factorialTail(int n, long acc) {
if (n <= 1) return acc;
return factorialTail(n - 1, n * acc); // Recursion is the last step
}
// Call
long result = factorialTail(5, 1);
💡 Note: Java compilers don't automatically optimize tail recursion, but understanding this concept helps when learning other languages.
Memoization
Store computed results in a cache to avoid redundant calculations.
JAVA
import java.util.HashMap;
import java.util.Map;
public class MemoFibonacci {
private static Map<Integer, Long> cache = new HashMap<>();
public static long fibonacci(int n) {
if (n <= 1) return n;
// Check cache
if (cache.containsKey(n)) {
return cache.get(n);
}
// Compute and cache
long result = fibonacci(n - 1) + fibonacci(n - 2);
cache.put(n, result);
return result;
}
public static void main(String[] args) {
System.out.println(fibonacci(50)); // 12586269025
}
}
❓ Frequently Asked Questions
Q When should I use recursion vs loops?
A Use loops for simple problems—they're more efficient. Recursion is suitable for naturally recursive problems like tree structures and divide-and-conquer algorithms.
Q When should I use static methods?
A Use static methods for utility methods that don't need to access object state. Use instance methods when you need to access object state.
Q What's the difference between method references and Lambda?
A Method references are a shorthand for Lambda, making code more concise. However, not all Lambdas can be replaced with method references.
📖 Summary
- Recursion is when a method calls itself, requiring a base case
- Static methods belong to classes, instance methods belong to objects
- Utility classes typically only contain static methods
- Method references are a shorthand for Lambda expressions
- Recursion can be optimized with memoization
📝 Exercises
- Recursion practice: Use recursion to calculate the sum from 1 to n
- Recursion practice: Use recursion to reverse a string
- Utility class: Write a math utility class with methods for maximum, minimum, average, etc.
Next Lesson
In the next lesson, we'll learn about the String Class — common string operations.



