经常会听到递归的概念,然而什么是递归呢?“程序调用自身的编程技巧称为递归( recursion)”emmmm,,,,太抽象了“递归”这个东西想要理解,灵活运用,还真要下翻功夫,自己感受下调用流程。
谈谈java方法的调用模型细节
java中方法的调用是基于栈模型的,方法被调用方法入栈,方法被调用完毕方法出栈。至于方法入栈时的栈帧及其相关参数、返回值记录信息我们就不在这唠叨了有兴趣的小伙伴可以深入了解下。接下来我们就以一个栗子来熟悉下方法被调用的流程。
1、栗子
/**
* Created by sunnyday on 2020/12/18 11:02
*/
public class TestMethodInvoke {
public static void main(String[] args) {
System.out.println("测试方法调用栈:"+test1());
}
private static int test1(){
int a = 1;
System.out.println("a="+a);
return test2(); // 1、return 此处会 invoke test2()方法,并标记返回值为test2方法返回结果。//5、收到test2的返回值,然后作为test1的返回值返回。
}
private static int test2(){
int a = 2;
System.out.println("a="+a);
return test3();// 2、return 此处会 invoke test3()方法,并标记返回值为test3方法返回结果。 // 4、收到test3的返回值,然后作为test2的返回值返回。
}
private static int test3(){
int a = 3;
System.out.println("a="+a);
return a; //3、return 此处未进行方法调用,直接返回定义的变量值,返回给被调用的位置(如上代码指针回到test2()中的test3方法调用处。结合debugger调试器&jvm方法调用理解)。然后方法出栈、方法销毁。
}
// log:
// a=1
// a=2
// a=3
// 测试方法调用栈:3
}
2、 对System.out.println(“测试方法调用栈:”+test1())的理解
宏观理解:test1的调用结果就是最后一个函数返回值。(一眼就可看出最终调用)
微观理解:最后一个函数吧结果回溯给上次调用。
收获认知:基于栈的方法调用就是不断入栈,最终回溯,返回调用信息。
递归认知
1、递归:程序调用自身的编程技巧称为递归( recursion)
2、递归的作用:具有回溯功能,和栈的作用一样。因为方法调用本身就是基于栈这种数据结构的。
3、递归本质:原来的问题转化为同一的更小的问题。
4、递归书写:
a、首先书写递归终止条件,即递归调用到底的情况。否则无限递归会使栈溢出。
b、递归调用自身
5、递归练习建议
a、找个demo分析下调用流程(初次接触很难想通,可借助debug查看)
b、拆解递归方法,跑下demo便于理解(必要的话再进行debug下查看)
c、做几道题联系下(如数字求和、数组二分查找)
(1)找个递归栗子
如上是一个简单的递归栗子,我们可以思考下上述的调用流程,思考不通,可借助debug来思考。
(2)递归栗子拆解
其实不断调用自身和不断调用其他方法(方法体逻辑一致)是一样的,因此我们可以分解其调用过程,来明白递归的功能:回溯功能,和栈数据结构一样。
/**
* Created by sunnyday on 2020/11/13 9:23
* 递归调用的模拟
*/
public class Recursion {
public static void main(String[] args) {
System.out.println("函数最终返回值:" + test(5));
}
static int test(int num) { // num = 5
if (0 == num) {
return 0;
} else {
num--;
test4(num);//test4(4)
}
System.out.println("num:" + num+" ");
return num;
}
static int test4(int num) {
if (0 == num) {
return 0;
} else {
num--;
test3(num);// test3(3)
}
System.out.println("num:" + num+" ");
return num;
}
static int test3(int num) {
if (0 == num) {// start
return 0;
} else {
num--;
test2(num);// test2(2)
}
System.out.println("num:" + num+" ");
return num;
}
static int test2(int num) {
if (0 == num) {// start
return 0;
} else {
num--;
test1(num);// test1(1)
}
System.out.println("num:" + num+" ");
return num;
}
static int test1(int num) {
if (0 == num) {// start
return 0;
} else {
num--;
test0(num);// test0(0)
}
System.out.println("num:" + num+" ");
return num;
}
static int test0(int num) {
if (0 == num) {
return 0; // return 0,递归到底了。以下代码不执行了,直接返回调用test0的位置(如上Test1方法中)然后依次返回。
} else {
num--;
//test(num);
}
System.out.println("num:" + num+" ");
return num;
}
}
(3)demo练习
1、1-100求和 递归实现
2、斐波那契数列递归实现
总结:普通函数调用与递归调用细节
普通方法:使用debugger 调试会依次看到方法返回的调用处。
递归方法:直接在return 递归时多次返回调用结果。可以看做省略了传递过程。