请解释下述Java递归函数的运行逻辑及输入值存储位置
这段Java递归代码的完整运行逻辑
核心机制:输入值的存储位置
你疑惑的“所有输入的x存在哪里”,答案非常明确:所有非终止值的输入x,全部存储在JVM虚拟机栈的对应栈帧的局部变量表中,不存在任何外部集合/数组来存储这些值。
Java每次调用方法时,都会在当前线程的虚拟机栈中压入一个独立的栈帧,每个栈帧会完整保存这次方法调用的所有局部变量、方法参数、返回地址等信息,只要当前栈帧没有被弹出,里面存储的变量就会一直保留,不会被回收。这里需要注意:每一层栈帧里的in形参,存的是同一个Scanner对象的引用,不会因为递归调用创建多个独立的输入流实例。
全流程执行步骤
整个程序的运行会分成两个完全独立的阶段,这也是你感觉“终止前不打印,终止后才输出”的核心原因:
第一阶段:递归压栈,只采集不判断
从main方法第一次触发multipleThree调用开始,程序会不断重复以下动作,直到用户输入-1:
- 打印输入提示,读取用户输入的整数x
- 如果x不等于-1,直接优先执行下一层
multipleThree的递归调用,当前这一层会卡在递归调用的代码行,暂停执行后面的逻辑,直到下一层调用返回 - 每一层递归对应的栈帧,会把当前读到的x值存在自己的局部变量表里,暂时不会做任何整除判断、打印操作
举个实际输入的栈压入顺序示例,假设用户依次输入3、6、9、-1:
- 栈从下到上依次压入:main栈帧 → 第1层multipleThree(x=3)→ 第2层multipleThree(x=6)→ 第3层multipleThree(x=9)→ 第4层multipleThree(x=-1)
第二阶段:递归弹栈,倒序判断输出
当用户输入-1触发终止条件后,程序会从最上层的x=-1的栈帧开始逐层返回:
- x=-1对应的栈帧直接触发if分支return,没有任何递归操作,直接弹栈
- 程序逐层回到之前暂停的位置,也就是每一层递归调用语句的下一行,这时候才会执行当前栈帧里存的x值的判断逻辑:如果x是3的倍数,就打印输出
- 这个过程是严格按照「后输入先判断」的倒序执行的,比如刚才的输入示例,弹栈顺序是:
- 回到x=9的层:判断9是3的倍数,打印
- 回到x=6的层:判断6是3的倍数,打印
- 回到x=3的层:判断3是3的倍数,打印
- 所有栈帧弹完后程序退出
关键注意点
- 这段代码的执行顺序核心来自代码块的书写顺序:
multipleThree(in);递归语句写在了整除判断的前面,属于典型的后序递归。如果把这两行代码位置调换,就会变成「读一个数就判断打印一个数」,和普通while循环的执行顺序完全一致 - 这种递归写法存在天生缺陷:如果用户输入的非-1数值过多,递归深度超过JVM栈的最大容量,会直接抛出
StackOverflowError栈溢出错误,远不如用集合存值再循环遍历的写法稳定
内容的提问来源于stack exchange,提问作者Devendra Adhikari
相关产品推荐
相关产品推荐

