07-数组入门必看!Java数组的内存分析02

一、【核心概念/问题引入】

1.1 先搞懂核心:数组内存到底是什么?

把内存比作生活中的空间,一秒就懂了:

  • 栈内存:像随身口袋,空间小但取用快,放的是临时物品(方法中的局部变量)
  • 堆内存:像大型仓库,空间大但取用慢,放的是长期存储的物品(new 出来的对象)
    官方定义是:Java 程序运行时,内存分为栈内存和堆内存,数组名存储在栈内存中,作为引用指向堆内存中真正的数组数据。

1.2 为什么需要理解数组内存?

  • 没有理解内存:遇到空指针异常、索引越界等问题时束手无策,只能靠试错解决
  • 理解了内存:能够从根本上理解数组的工作原理,轻松定位和解决内存相关问题

二 、【核心知识点详解】

2.1 Java 内存区域核心划分

Java 虚拟机(JVM)在运行时会将内存划分为不同区域,其中与数组操作最相关的是以下三个:

内存区域 作用
方法区 存储已加载的类信息、常量、静态变量等(比如 Test 类的结构信息)。
栈内存 每个方法执行时创建 "栈帧",存储局部变量表(基本类型、对象引用)。
堆内存 存储对象实例(包括数组对象,因为数组在 Java 中也是对象)。

2.2 一维数组:完整内存生命周期

将以这个代码为例,逐行分析代码,结合内存图解来理解变化过程。

ini 复制代码
public class Test {    
    public static void main(String[] args) {                
        int[] arr = {11, 22, 33};        
        arr[0] = 44;        
        arr[1] = 55;        
        arr[2] = 66;        
        System.out.println(arr[0]);        
        System.out.println(arr[1]);        
        System.out.println(arr[2]);    
    }
}

1. 类加载:Test 类进入方法区

当程序启动时,JVM 会先将 Test 类加载到方法区 ,存储类的结构信息(比如 main 方法的定义)。

此时内存状态:

2. 执行 main 方法:栈帧入栈

main 方法开始执行时,JVM 会在栈内存 中为 main 方法创建一个 "栈帧",栈帧的局部变量表用于存储方法内的变量(比如 args 和后续的 arr

此时内存状态:

3. 执行 int[] arr = {11, 22, 33};:创建数组对象

这一步是核心,会同时涉及栈内存堆内存

  1. 堆内存 :分配一块连续空间,创建长度为 3 的 int 数组对象,并初始化元素为 11, 22, 33(数组对象会包含 "长度" 信息和元素槽位)。
  2. 栈内存 :将堆中数组的内存地址(引用)赋值给局部变量 arr,让 arr 指向堆中的数组。
    此时内存状态(关键变化):
4. 执行 arr[0] = 44; arr[1] = 55; arr[2] = 66;:修改数组元素

由于 arr 存储的是堆中数组的引用 ,因此通过 arr[索引] 可以直接定位到堆中数组的元素,并修改其值:

  • arr[0] = 44:将堆中数组索引 0 的值从 11 改为 44
  • arr[1] = 55:将索引 1 的值从 22 改为 55
  • arr[2] = 66:将索引 2 的值从 33 改为 66
    此时内存状态(堆中数组元素被修改):

5. 执行 System.out.println(...):打印数组元素

通过 arr 引用找到堆中数组,取出当前索引对应的值(44, 55, 66),输出到控制台。

2.3 两个数组指向相同内存

这段代码的核心是 "引用赋值" ------ 两个数组变量 array1array2 最终会指向堆内存中的同一个数组对象。我们结合内存区域逐行分析,并配上直观的图解。

ini 复制代码
public class Test { 
	public static void main(String[] args) { 
		int[] array1 = {11, 22, 33}; 
		int[] array2 = array1; 
		array2[0] = 100; 
		System.out.println(array1[0]); 
		System.out.println(array2[0]); 
	} 
}

1. 类加载:Test 类进入方法区

程序启动,JVM 将 Test 类加载到方法区

2. 执行 main 方法:栈帧入栈

main 方法开始执行,在栈内存 创建栈帧,局部变量表预留 array1array2 的位置(此时还未赋值)。

3. 执行 int[] array1 = {11, 22, 33};:创建第一个数组

这一步同时操作栈和堆:

  • 堆内存 :分配空间,创建长度为 3 的 int 数组,初始值 11, 22, 33,地址假设为 0x666
  • 栈内存 :将堆中数组的地址 0x666 赋值给 array1,让 array1 指向该数组。

4. 执行 int[] array2 = array1;:关键!引用赋值

这是代码的核心 ------ array2 复制了 array1 的引用(地址) ,而不是复制数组对象本身。

结果:array2array1 都指向堆内存中同一个数组 (地址 0x666)。

5. 执行 array2[0] = 100;:修改数组元素

由于 array2array1 指向同一个数组,通过 array2 修改元素时,堆中唯一的数组对象会被改变

结果:索引 0 的值从 11 变为 100

6. 执行打印语句:输出结果

通过 array1array2 访问的是同一个堆数组,因此:

  • System.out.println(array1[0]); → 输出 100
  • System.out.println(array2[0]); → 输出 100

三、总结

3.1 、Java 内存区域的核心分工(基础)

内存区域 与数组相关的核心作用
方法区 存储类信息(如Test类的结构),与数组对象的具体运行时数据无关。
栈内存 存储局部变量表,其中的array1、 array2 等变量是数组的引引用(堆中数组的地址)。
堆内存 存储数组对象本身(因为数组在Java中是对象),包括数组的长度和具体元素值。

3.2 两段代码的核心行为总结

1. 第一段代码:单引用操作数组

  • 创建数组int[] arr = {11, 22, 33}; → 堆中分配空间创建数组,栈中 arr 引用指向堆数组地址。
  • 修改元素arr[0] = 44; → 通过栈中 arr 的引用,直接修改堆中数组的元素值。

2. 第二段代码:多引用指向同一数组

  • 引用赋值int[] array2 = array1; → 不是复制堆中的数组对象,而是复制堆数组的地址array2,导致 array1array2 指向堆中同一个数组
  • 联动修改array2[0] = 100; → 由于两个引用指向同一堆对象,通过 array2 修改元素后,array1 访问时也会看到变化。

3.3 必须记住的核心规则

  1. 数组是对象,永远在堆里 :无论代码怎么写,数组实例一定存储在堆内存中。
  2. 数组变量是 "引用",不是对象本身 :栈中的 arrarray1array2 只是堆中数组的 "地址指针",不是数组本身。
  3. 引用赋值 = 共享同一个对象array2 = array1 会让两个引用绑定到同一个堆数组,修改其中一个,另一个必然受影响。
  4. 通过引用修改的是堆中的对象arr[0] = 44array2[0] = 100,本质都是直接修改堆中数组的元素值。
相关推荐
前端技术2 小时前
ArkTS第三章:声明式UI开发实战
java·前端·人工智能·python·华为·鸿蒙
带刺的坐椅2 小时前
RFC 9535:JSONPath 的标准化之路
java·json·jsonpath·snack4·rfc9535
神の愛2 小时前
java日志功能
java·开发语言·前端
喵个咪2 小时前
Go 语言 CMS 横评:风行 GoWind 对比传统 PHP/Java CMS 核心优势
前端·后端·cms
面向Google编程2 小时前
从零学习Kafka:位移与高水位
大数据·后端·kafka
却话巴山夜雨时i2 小时前
互联网大厂Java面试:从Spring到微服务的全栈挑战
java·spring boot·redis·微服务·面试·kafka·技术栈
ch.ju2 小时前
Java程序设计(第3版)第二章——java的数据类型:字符 char
java
尘世壹俗人2 小时前
idea提交git版本由于中文文件名卡死不动
java·git·intellij-idea
Honmaple2 小时前
驾驭AI的黄金缰绳:Harness Engineering引领2026工程范式变革
后端