1. 递归调用

所谓递归方法的调用,即在定义的方法体中运行方法本身,下面示例中run方法体又调用了run方法:

  1. public class RecursiveMethodCall {
  2. public void run() {
  3. run();
  4. System.out.println("123");
  5. }
  6. public static void main(String[] args) {
  7. RecursiveMethodCall rmc = new RecursiveMethodCall();
  8. rmc.run();
  9. }
  10. }

2. 内存溢出

上面示例代码是可以正常编译的,但是运行就会报内存溢出(StackOverflowError)错误,其原因在于方法地递归调用是一个死循环,导致栈中不断地开辟新空间给run方法,这也就是我们常说的压栈

细心的同学可能会问,当初我们在学习流程控制时,while (true)循环不也是死循环么,它为什么可以正常执行呢?

  1. public class RecursiveMethodCall {
  2. public void run() {
  3. System.out.println("123");
  4. }
  5. public static void main(String[] args) {
  6. RecursiveMethodCall rmc = new RecursiveMethodCall();
  7. while (true) {
  8. rmc.run();
  9. }
  10. }
  11. }

显然,在主程序中所调用的rmc.run方法,其在栈中指向的地址是一样的,并不会因为循环而去开辟新的空间。但是,run方法中递归调用run方法,是会开辟新的空间给递归的run方法,所以内存会溢出。

3. 递归经典案例一:斐波那契数列

上面方法的递归调用其实是一个错误的示例,演示它的目的并不是告诉大家不要进行方法的递归调用。在某些情况下,使用方法的递归调用可以极大地减少代码量以及提高运行效率,接下来我们就介绍一个递归经典案例——斐波那契数列。

:::info 🧰 斐波那契数列
—————————————
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610…. :::

  1. public class Recursion {
  2. public long fibonacci(long n) {
  3. if (n == 1 || n == 2) {
  4. return 1;
  5. } else {
  6. return fibonacci(n - 1) + fibonacci(n - 2);
  7. }
  8. }
  9. public static void main(String[] args) {
  10. Recursion recursion = new Recursion();
  11. int n = 3;
  12. long number = recursion.fibonacci(n);
  13. System.out.println(n + ": " + number);
  14. }
  15. }

4. 递归经典案例二:阶层

  1. public class Recursion {
  2. public long factorial(long n) {
  3. if (n == 1) {
  4. return 1;
  5. } else {
  6. return n * factorial(n - 1);
  7. }
  8. }
  9. public static void main(String[] args) {
  10. Recursion recursion = new Recursion();
  11. int n = 3;
  12. long number = recursion.factorial(n);
  13. System.out.println(n + ": " + number);
  14. }
  15. }