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

 更新时间:2021年10月08日 09:54:09   作者:宁海没有七号公园  
关于递归是什么,简单的说: 递归就是方法自己调用自己,每次调用时 传入不同的变量.递归有助于编程者解决复杂的问题,同时可以让代码变得简洁

方法的递归调用

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); //尝试输出看看
    }
}

代码截图:

在这里插入图片描述

运行结果:

在这里插入图片描述

结果分析:

为了看起来比较规范,首先我们先简单画出 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方法结束。
  • 具体机制见下图。

在这里插入图片描述

接上图~~

在这里插入图片描述

到这里,我们大概就能懂为啥是先打印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); //尝试输出看看
    }
}

代码截图:

在这里插入图片描述

运行结果:

在这里插入图片描述

尝试自己分析一下⑧,简单来说就是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);

    }
}

运行结果:

在这里插入图片描述

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

在这里插入图片描述

递归的重要规则:

  • 执行一个方法时,就创建一个相应的新的受保护的独立空间 (栈空间/栈帧);
  • 方法的局部变量是独立的,不会相互影响,比如前面多次提到的n变量;
  • 如果方法中使用的是引用类型变量,比如数组或者String类型变量,就会共享该引用类型的数据 (指向同一堆空间);
  • 递归必须向退出递归的条件逼近,否则就是无限递归,会出现栈溢出Stack Overflow Error,也就是死循环;
  • 当一个方法执行完毕,或者遇到return时,就会返回,返回的规则遵守谁调用就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就是执行完毕,还给操作系统,具体是啥时候还给操作系统这要看当时的环境;

到此这篇关于Java 关于递归的调用机制精细解读的文章就介绍到这了,更多相关Java 递归内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring事务框架之TransactionStatus源码解析

    Spring事务框架之TransactionStatus源码解析

    这篇文章主要为大家介绍了Spring事务框架之TransactionStatus源码示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • java捕获AOP级别的异常并将其传递到Controller层

    java捕获AOP级别的异常并将其传递到Controller层

    如何在一个现代的Java应用中,捕获AOP(面向切面编程)级别的异常,并将这些异常传递到Controller层进行合适的处理,异常处理在构建可靠的应用程序中起着关键作用,而AOP则可以帮助我们更好地管理和组织代码,我们将深入研究如何结合AOP和异常处理来构建健壮的应用
    2023-09-09
  • Spring Boot Filter 过滤器的使用方式

    Spring Boot Filter 过滤器的使用方式

    这篇文章主要介绍了Spring Boot Filter 过滤器的使用方式,文章通过围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-09-09
  • JavaScript 与 Java 区别介绍  学java怎么样

    JavaScript 与 Java 区别介绍 学java怎么样

    JavaScript 是一种嵌入式脚本文件,直接插入网页,有浏览器一边解释一边执行。而java 语言不一样,他必须在JAVA虚拟机上运行。而且事先需要进行编译。接下来脚本之家小编给大家揭晓js与java区别,感兴趣的朋友一起看看吧
    2016-09-09
  • java报错之springboot3+vue2项目web服务层报错总结

    java报错之springboot3+vue2项目web服务层报错总结

    java入门学习,随手记录一下开发过程中产生的报错,有些错误是网上搜索再加上自己尝试,随手引用了一些其他人的记录,也是留给自己看的,或是希望能对其他初学者有帮助

    2023-06-06
  • Java多线程中的Executor框架解析

    Java多线程中的Executor框架解析

    这篇文章主要介绍了Java多线程中的Executor框架解析,Executor 框架是 Java5 之后引进的,在 Java 5 之后,通过 Executor 来启动线程比使用 Thread 的 start 方法更好,除了更易管理,效率更好,需要的朋友可以参考下
    2023-12-12
  • java编译时指定classpath的实现方法

    java编译时指定classpath的实现方法

    在Java编程中,classpath是用于指定Java虚拟机在运行时查找类文件的路径,本文主要介绍了java编译时指定classpath的实现方法,具有一定的参考价值,感兴趣的可以了解一下
    2023-10-10
  • 基于mybatis-plus-generator实现代码自动生成器

    基于mybatis-plus-generator实现代码自动生成器

    这篇文章专门为小白准备了入门级mybatis-plus-generator代码自动生成器,可以提高开发效率。文中的示例代码讲解详细,感兴趣的可以了解一下
    2022-05-05
  • Spring Boot的filter(过滤器)简单使用实例详解

    Spring Boot的filter(过滤器)简单使用实例详解

    过滤器(Filter)的注册方法和 Servlet 一样,有两种方式:代码注册或者注解注册,下面通过实例给大家介绍Spring Boot的filter(过滤器)简单使用,一起看看吧
    2017-04-04
  • SpringBoot开发教程之AOP日志处理

    SpringBoot开发教程之AOP日志处理

    现在凡是企业级的或者稍微大点项目,基本都需要日志管理,下面这篇文章主要给大家介绍了关于SpringBoot开发教程之AOP日志处理 的相关资料,需要的朋友可以参考下
    2021-10-10

最新评论