方法进阶
本课深入学习方法的高级特性,包括递归、静态方法和方法引用。
递归
递归是方法调用自身的编程技巧。
递归三要素
| 要素 | 说明 |
|---|---|
| 基准条件 | 递归终止的条件 |
| 递归调用 | 方法调用自身 |
| 问题缩小 | 每次递归问题规模减小 |
示例:计算阶乘
JAVA
public class Factorial {
public static long factorial(int n) {
// 基准条件
if (n <= 1) {
return 1;
}
// 递归调用
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
}
}
递归过程
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
示例:斐波那契数列
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) + " ");
}
// 输出:0 1 1 2 3 5 8 13 21 34
}
}
⚠️ 注意: 简单递归效率低,可以用记忆化优化。对于斐波那契数列,迭代方式更高效。
示例:递归遍历目录
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修饰的方法,属于类,不需要创建对象即可调用。
JAVA
public class MathUtils {
public static int add(int a, int b) {
return a + b;
}
}
// 调用
int sum = MathUtils.add(3, 5);
实例方法
没有static的方法,属于对象,需要创建对象才能调用。
JAVA
public class Calculator {
public int add(int a, int b) {
return a + b;
}
}
// 调用
Calculator calc = new Calculator();
int sum = calc.add(3, 5);
对比
| 特性 | 静态方法 | 实例方法 |
|---|---|---|
| 关键字 | static |
无 |
| 调用方式 | 类名.方法() |
对象.方法() |
| 访问成员 | 只能访问静态成员 | 可以访问所有成员 |
| this关键字 | 不能使用 | 可以使用 |
示例:静态方法与实例方法
JAVA
public class MethodTypeDemo {
private int count = 0;
// 静态方法
public static int add(int a, int b) {
return a + b;
}
// 实例方法
public void increment() {
count++;
}
public int getCount() {
return count;
}
public static void main(String[] args) {
// 静态方法直接调用
System.out.println(add(3, 5)); // 8
// 实例方法需要对象
MethodTypeDemo demo = new MethodTypeDemo();
demo.increment();
demo.increment();
System.out.println(demo.getCount()); // 2
}
}
工具类设计
工具类通常只包含静态方法,不需要创建对象。
示例:字符串工具类
JAVA
public class StringUtils {
// 判断是否为空
public static boolean isEmpty(String str) {
return str == null || str.isEmpty();
}
// 判断是否为空白
public static boolean isBlank(String str) {
return str == null || str.trim().isEmpty();
}
// 反转字符串
public static String reverse(String str) {
if (str == null) return null;
return new StringBuilder(str).reverse().toString();
}
// 首字母大写
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
}
}
方法引用
Java 8引入方法引用,是Lambda的简写形式。
四种方法引用
| 类型 | 语法 | 示例 |
|---|---|---|
| 静态方法 | 类名::静态方法 |
Math::abs |
| 实例方法 | 对象::实例方法 |
System.out::println |
| 特定类的实例方法 | 类名::实例方法 |
String::length |
| 构造方法 | 类名::new |
ArrayList::new |
示例:方法引用
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表达式
names.forEach(name -> System.out.println(name));
// 方法引用(更简洁)
names.forEach(System.out::println);
// 静态方法引用
List<Integer> numbers = Arrays.asList(-3, -1, 0, 2, 5);
numbers.stream()
.map(Math::abs)
.forEach(System.out::println);
// 输出:3 1 0 2 5
}
}
示例:构造方法引用
JAVA
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
public class ConstructorRefDemo {
public static void main(String[] args) {
// Lambda表达式
Supplier<List<String>> listFactory = () -> new ArrayList<>();
// 构造方法引用
Supplier<List<String>> listFactory2 = ArrayList::new;
List<String> list = listFactory2.get();
list.add("Hello");
System.out.println(list); // [Hello]
}
}
递归优化
尾递归优化
尾递归是指递归调用是函数的最后一个操作。
JAVA
// 普通递归(不是尾递归)
public static long factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1); // 递归后还要乘n
}
// 尾递归版本
public static long factorialTail(int n, long acc) {
if (n <= 1) return acc;
return factorialTail(n - 1, n * acc); // 递归是最后一步
}
// 调用
long result = factorialTail(5, 1);
💡 注意: Java编译器不自动优化尾递归,但理解这个概念对学习其他语言有帮助。
记忆化递归
用缓存存储已计算的结果,避免重复计算。
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;
// 检查缓存
if (cache.containsKey(n)) {
return cache.get(n);
}
// 计算并缓存
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
}
}
❓ 常见问题
Q 递归和循环怎么选?
A 简单问题用循环更高效。递归适合树形结构、分治算法等天然递归的问题。
Q 什么时候用静态方法?
A 工具方法、不需要访问对象状态的方法用静态。需要访问对象状态用实例方法。
Q 方法引用和Lambda有什么区别?
A 方法引用是Lambda的简写,更简洁。但不是所有Lambda都能用方法引用替代。
📖 小节
- 递归是方法调用自身,需要基准条件
- 静态方法属于类,实例方法属于对象
- 工具类通常只包含静态方法
- 方法引用是Lambda的简写形式
- 递归可以用记忆化优化性能
📝 作业
- 递归练习: 用递归计算1到n的和
- 递归练习: 用递归反转字符串
- 工具类: 编写一个数学工具类,包含求最大值、最小值、平均值等方法
下一课
下一课我们将学习 String类,了解字符串的常用操作。



