Java 关于递归的调用机制精细解读

作者:宁海没有七号公园 时间:2023-01-17 04:42:41 

方法的递归调用

1. 基本介绍:

简单地说,递归就是方法自己调用自己,每次调用时传入不同的变量,递归有助于编程者解决复杂问题的同时让代码变得简洁,化繁为简是其核心思想。

2. 递归能解决什么问题?

  • 各种经典数学问题,如:八皇后问题,汉诺塔(河内塔),阶乘问题,迷宫问题,青蛙跳台阶,球和篮子的问题(Google编程大赛);

  • 各种算法中也会使用到递归思想,比如快速排序(quick sort),归并排序(merge sort),二分查找(binary search),分治算法(divide and conquer)等;

  • 用栈解决的问题换成递归实现 --> 递归代码比较简洁;

3. 递归举例分析:

3.1 打印问题:

我们来看一哈这一段代码:


package com.recursion;

class Test{
   public void test(int n) {
       if (n > 2) {
           test(n - 1);
       }
       System.out.println("n=" + n);
   }
}

public class Recursion {
   public static void main(String[] args) {
     Test t1 = new Test();
     t1.test(4); //尝试输出看看
   }
}

代码截图:

Java 关于递归的调用机制精细解读

运行结果:

Java 关于递归的调用机制精细解读

结果分析:

为了看起来比较规范,首先我们先简单画出 JVM内存区域 ,这里只涉及到栈空间,堆空间和方法区:

  • 首先看到main方法(程序的入口),有C/C++基础的小伙伴们应该晓得,我们知道在调用方法时,在栈空间中会创建相应的栈帧,main方法也是一个方法,也会被其他进程调用(在Linux中main函数有_start函数调用 ,这里不在展开,感兴趣的小伙伴自行了解⑧),所以自然也会形成main栈帧。此时new了一个对象,此对象会在堆中创建,在栈中的引用变量会指向此堆空间,也就是保存了此对象的地址,如图。

  • main方法中调用了test方法,所以在栈中也会创建test栈帧,此时我们传入的n为4,接下来判断n大于4吗?很明显大于4,所以在test栈中又要调用test(n-1),也就是调用test(3),既然调用了方法,那便又要在栈中创建相应的栈帧,以此类推。

  • 当调用的方法为test(2)时,此时判断n大于2显然为false,此时便要执行方法的最后一句语句,也就是sout打印语句,此时便会先打印2,打印完2之后方法结束( * 作系统回收/资源销毁),返回到前一个调用此方法的栈帧中,也是以此类推,直到main方法结束。

  • 具体机制见下图。

Java 关于递归的调用机制精细解读

接上图~~

Java 关于递归的调用机制精细解读

到这里,我们大概就能懂为啥是先打印2,再打印3,最后才打印4了。

我们再来进一步拓展一下上述问题:

源代码:


package com.recursion;

class Test{
   public void test(int n) {
       if (n > 2) {
           test(n - 1);
       } else {  //唯一区别就是加了else
           System.out.println("n=" + n);
       }
   }
}

public class Recursion {
   public static void main(String[] args) {
     Test t1 = new Test();
     t1.test(4); //尝试输出看看
   }
}

代码截图:

Java 关于递归的调用机制精细解读

运行结果:

Java 关于递归的调用机制精细解读

尝试自己分析一下⑧,简单来说就是if执行了else就不执行,else执行了说明if也没执行。

3.2 阶乘问题:

源代码:


package com.recursion;

class Test01 {
   public int factorial(int n) {
       if (n == 1) {
           return 1;
       } else {
           return factorial(n - 1) * n;
       }
   }
}

public class Factorial {
   public static void main(String[] args) {
       Test01 test = new Test01();
       int ret = test.factorial(5);
       System.out.println("ret=" + ret);

}
}

运行结果:

Java 关于递归的调用机制精细解读

结果分析:大体上都跟前面的打印例子差不多,都是调用自身时在栈上开辟相应的栈帧,前面忘说了,栈帧其实会二次开辟的,啥意思呢,就是说调用方法时先在栈上分配一块较大的空间,也就是栈帧,而在栈帧内部还会进行一次具体的内存划分,具体到每一个变量。每个栈帧结束后将返回值返回给上一个栈帧,以此类推就能清晰明了的弄清楚递归的调用机制。

Java 关于递归的调用机制精细解读

递归的重要规则:

  • 执行一个方法时,就创建一个相应的新的受保护的独立空间 (栈空间/栈帧);

  • 方法的局部变量是独立的,不会相互影响,比如前面多次提到的n变量;

  • 如果方法中使用的是引用类型变量,比如数组或者String类型变量,就会共享该引用类型的数据 (指向同一堆空间);

  • 递归必须向退出递归的条件逼近,否则就是无限递归,会出现栈溢出Stack Overflow Error,也就是死循环;

  • 当一个方法执行完毕,或者遇到return时,就会返回,返回的规则遵守谁调用就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就是执行完毕,还给操作系统,具体是啥时候还给操作系统这要看当时的环境;

来源:https://blog.csdn.net/b_ingram/article/details/120638507

标签:Java,递归,机制
0
投稿

猜你喜欢

  • 浅谈JAVA设计模式之享元模式

    2021-09-22 10:12:06
  • Spring MVC 使用支付宝接口完成在线支付的示例代码

    2023-11-29 04:07:55
  • 基于Unity制作一个简易的计算器

    2023-02-18 10:02:39
  • Java实现年兽大作战游戏详解

    2023-11-08 04:28:05
  • 浅谈Java多线程实现及同步互斥通讯

    2022-11-17 17:50:53
  • Android实现简单旋转动画

    2023-11-07 09:50:58
  • 简单实现Android弹出菜单效果

    2023-01-11 18:58:46
  • C#学习笔记- 浅谈数组复制,排序,取段,元组

    2021-10-21 00:50:58
  • Android简单实现弹幕效果

    2022-08-12 01:24:08
  • 微信开发--自定义菜单查询返码乱码的解决方法

    2023-11-25 04:47:55
  • Android性能优化以及数据优化方法

    2021-09-02 03:46:18
  • Kotlin 和 Java 混合开发入门教程

    2023-07-06 01:41:51
  • Java实现简易生产者消费者模型过程解析

    2023-12-03 01:33:26
  • Java语言读取配置文件config.properties的方法讲解

    2023-09-29 14:45:51
  • Java 使用Docker时经常遇到的五个问题

    2023-12-10 07:20:21
  • Android RichText 让Textview轻松的支持富文本(图像ImageSpan、点击效果等等类似QQ微信聊天)

    2022-03-18 17:16:48
  • 基于Java实现Json文件转换为Excel文件

    2022-08-04 23:53:15
  • Mybatis中and和循环or混用操作(or转换成in)

    2023-09-19 11:08:34
  • Unity实现仿3D轮转图效果

    2023-11-24 12:26:56
  • SpringBoot 集成 activiti的示例代码

    2023-01-22 10:22:03
  • asp之家 软件编程 m.aspxhome.com