You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

递归打印数组逆序触发IndexOutOfBoundException?求内存分配等技术解析

递归逆序打印数组的索引越界问题解析

问题场景

写递归函数逆序打印数组元素时,发现两种写法结果完全不同:在打印语句前调用递归会触发IndexOutOfBoundException(索引越界),先执行打印再调用递归则能正常运行。原本猜测和deepcopy有关,但不清楚真正原因,同时想了解内存分配、static/non-static、全局/局部变量、deepcopy在栈中的相关逻辑。

调用代码

int[] arr = {1,2,3};
int n = arr.length;
print(arr,n-1);

报错的递归函数

public static void print(int[] arr, int n)
{
    if(n<0)
        return;

    print(arr,--n);
    
    System.out.println(arr[n]);
}

正常运行的递归函数

public static void print(int[] arr, int n)
{
    if(n<0)
        return;
    
    System.out.println(arr[n]);

    print(arr,--n);
}

报错原因分析

问题核心出在前置自减运算符--n的执行时机,和deepcopy完全无关:

  • 报错的函数中,先执行print(arr, --n):这里会先把当前栈帧里的n减1,再把新值作为参数传入下一层递归。比如当当前n=0时,先把n改成-1,调用递归后触发return,回到当前栈帧时,n已经是-1,执行System.out.println(arr[n])就会访问数组的负索引,直接越界。
  • 正常运行的函数中,先打印arr[n]再调用递归:当n=0时,先打印数组的第0个元素,再执行--n得到-1传入递归,此时下一层递归直接return,不会再用到修改后的n去访问数组,自然不会报错。

另外要注意:Java中基本类型参数是值传递,每一层递归的栈帧里都会有独立的n变量,但--n是修改当前栈帧的n后再传参,不是修改下一层的n。

相关内存概念讲解

1. 栈内存分配

Java里每个线程都会分配一个独立的栈空间,每次调用方法时,JVM会在栈上创建一个栈帧,包含局部变量表、操作数栈、方法返回地址等信息。方法执行完毕后,栈帧会被弹出栈,释放对应的内存。递归调用就是不断往栈里压入新栈帧,直到触发终止条件,再依次弹出栈帧执行后续逻辑。

2. 局部变量 vs 全局变量

  • 局部变量:定义在方法内部的变量,存放在当前方法栈帧的局部变量表中,每个方法调用的栈帧都有自己的局部变量副本,相互独立。
  • 全局变量:也就是类的成员变量,非static的成员变量存在堆内存的对象实例中,每个对象有自己的一份;static成员变量属于类,存放在元空间(原方法区),所有对象实例共用。

3. static/non-static 变量

  • static变量:属于类本身,不需要实例化对象就能访问,生命周期和类一致,存储在元空间,不占用栈内存。
  • non-static变量:属于对象实例,只有创建对象后才会分配内存,存在堆内存的对象中,每个实例的non-static变量都是独立的。

4. deepcopy 与栈的关系

deepcopy和栈没有直接关联:它是指完全复制一个对象的所有层级结构,包括对象内部的引用类型成员,复制后的对象和原对象在堆内存中是完全独立的两个实例。栈里存储的只是指向堆对象的引用地址,deepcopy操作不会改变栈的结构,只是在堆里生成新对象后,栈里的引用指向新的地址而已。

内容的提问来源于stack exchange,提问作者Dhruv Chopra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 18:23:11