当前位置:网站首页>如何从内存解析的角度理解“数组名实质是一个地址”?
如何从内存解析的角度理解“数组名实质是一个地址”?
2022-07-26 05:18:00 【碳基肥宅】
本文从Java语言的角度,探讨一维数组与二维数组的内存解析。
目录
一、内存的简化结构
下图即为内存的简化结构。在Java语言中,内存的存储分配是这样的:
栈:局部变量
堆:new出来的东西,如对象、数组等
方法区:包括静态域(static)和常量池(String的内容就存储在这里)

这张内存简化图非常重要,需要大家留有印象。
接下来我们在该图和结论的基础上,分步来看一维数组与二维数组的内存解析。
二、一维数组的内存解析
1. 分步解析
示例代码
public class Test{
public static void main(String args[]){
int[] arr;
arr = new int[10];
for ( int i=0; i<10; i++ ) {
arr[i] =2*i+1;
System.out.println(arr[i]);
}
}
}
step1 int[] arr;
此时在栈中创建出了变量arr:

step2 arr = new int[10];
接着使用new关键字,来创建一个一维数组。需要注意的是,基本数据类型数组在显式赋值之前, Java会自动给它们赋默认值。由于一维数组的每个元素都是int类型,因而默认值为0.

step3 arr[i] =2*i+1;
在for循环中遍历数组arr并为其元素赋值:

综合起来看,内存状态如下:
2. 综合解析
如图,创建数组并赋值的过程可以简化成如下示意图。

当声明数组 int[ ] arr 时,arr属于局部变量,在栈中创建。如上图中,int[ ] arr1和String[ ] arr2的操作执行后,在左侧的栈中创建了变量arr1与arr2. 若没有new的操作,实际上还未给数组开辟存储空间。
只有当通过new关键字创建数组对象后,系统才在堆中划分相应的存储空间,并依据数组元素的数据类型给新划分的空间自动赋初值。如上图中,在new int[4]后,堆区开辟了4个连续的存储空间,并赋值为0,而new String[3]后,堆区又开辟了3个存储空间用来存储String类型的数据,引用类型String默认初值为null.
同时,数组的地址(即数组首元素的地址,假设为0x12ab)赋值给栈区中的变量arr1,即变量arr1中存着数组的地址,通过该地址,arr1可以轻松地在堆中找到它对应的数组。
我们通过中括号 [ ] 来访问数组中的各个元素。我们执行arr1[0] = 10;这一代码时,实际便是根据栈内arr1中存着的地址,找到将堆区中的数组空间,并将第一个空间中的0改为了10.
而对于arr2[ ]数组,在new过之后又new了一次,第二次通过new String[5]开辟了一片5个存储空间的数组。这时变量arr2中原本存着的地址0x34ab被新地址0x78cd覆盖,arr2变量存放了新数组的地址。原数组在后续的某个时间内,将被自动回收。
当然,当栈区存有数组地址的变量arr1与变量arr2最终出栈后,在堆区划分的数组空间也将在之后被回收。
三、二(多)维数组的内存解析
1. 综合解析
二维数组是“数组的数组”,即一个一维数组中每个元素也是数组。由于数组既可以存储基本数据类型,也可以存储引用数据类型,因而“数组中存数组”的理解是可行的。
二维数组的创建过程与一维数组类似,这里便不再分步解析。我们直接来看内存解析图:

与一维数组的不同之处在于,二维数组中外层元素也用于存储地址。在创建二维数组时,除了在堆区中为外层元素(一维数组)开辟了存储空间外,还为内层元素开辟了存储空间。同时,内层元素的首元素地址返回给外层元素。
这样,数组名arr1与一维数组名arr1[ ] 像桥梁一样连接到内层元素arr1[ ][ ]。通过地址和存有地址的栈区变量arr1、堆区中的一维数组arr1[ ],我们可以轻松地访问到内层元素。
其中,内层元素的长度可以不相等。int[ ][ ] arr = new int[ ][ ]{ {1,2,3},{4,5},{6,7,8}};这样也是可行的,从图中就能清晰的看出,内层元素之间其实是相对独立的。
2. 默认初始化方式对初始值的影响
针对于 int[][] arr = new int[4][3]; 这样将内外层元素个数都指定了的初始化方式,外层元素的初始化值为地址值,内层元素的初始化值则与一维数组初始化情况相同(由元素的数据类型而决定)。
而针对 int[][] arr = new int[4][]; 这样省略内层元素长度的初始化方式而言,外层元素的初始化值为null(相当于未赋值的引用数据类型),内层元素则根本没有初始化值而言(空间还没开辟),不能调用,否则编译器将报错。
测试
//测试代码
public class ArrayTest {
public static void main(String[] args) {
int[][] arr = new int[4][3];
System.out.println(arr[0]); //[[email protected]
System.out.println(arr[0][0]); //0
//System.out.println(arr);
System.out.println("***********************");
float[][] arr1 = new float[4][3];
System.out.println(arr1[0]); //地址值
System.out.println(arr1[0][0]); //0.0
System.out.println("***********************");
String[][] arr2 = new String[4][2];
System.out.println(arr2[1]); //地址值
System.out.println(arr2[1][1]); //null
System.out.println("*********************");
double[][] arr3 = new double[4][];
System.out.println(arr3[1]); //null
// System.out.println(arr3[1][0]); //报错
}
}总结
单看数组名,实际上是一个创建在栈区的局部变量。 整个数组数据量可能较大,直接把数组内所有的元素都存放在栈区是不太妥当的。因而,数组的主体部分实际上开辟在堆区。
要想访问数组,若堆区的内容与栈区的数组名arr之间没有任何关系,是无法找到想要访问的数组内容的。因而,堆区会返回开辟的数组空间首元素的地址作为数组主体的地址,传给栈区的局部变量arr(它是一个数组名)。若是二维数组,内层元素的首元素地址会作为外层元素(也就是一维数组名)的内容。
通过地址和存储地址的“数组名”、“一维数组名”,我们可以一连串地找到我们想要访问的数组元素。
该部分内容我用文字表述可能不够简练,大家更多地可以看图,图为重点,通过图示来理解内存解析更好一些。
边栏推荐
- ALV报表流程图解
- FPGA question brushing sequence detection
- Your understanding of the "happen before principle" may be wrong?
- JVM Lecture 5: how to deal with peak push of vertical and horizontal data
- C语言力扣第42题之接雨水。四种方法——暴力、动态规划、栈、双指针
- Trend of the times - the rise of cloud native databases
- kubernetes install completed
- Improve reduce parallelism in shuffle operation
- OD-Paper【1】:Rich feature hierarchies for accurate object detection and semantic segmentation
- C语言力扣第41题之缺失的第一个正数。两种方法,预处理快排与原地哈希
猜你喜欢

CLM land surface process model

C language - Advanced pointer

第二讲 初识SLAM

SAP报表开发步骤

Princeton calculus reader 02 Chapter 1 -- composition of functions, odd and even functions, function images

Unity scene jump script

TZC 1283: simple sort - Comparative sort

Excel VBA: realize automatic drop-down filling formula to the last line

C语言函数

Day011 one dimensional array
随机推荐
元宇宙为服装设计展示提供数字化社交平台
Development to testing: a six-year road to automation from scratch
LeetCode链表问题——206.反转链表(一题一文学会链表)
Common solutions for distributed ID - take one
循环结构 practice
Leetcode linked list problem - 206. reverse linked list (learn linked list by one question and one article)
Test of countlaunch demo
JVM Lecture 5: how to deal with peak push of vertical and horizontal data
Date and time function of MySQL function summary
flex布局原理及常见的父项元素
No background, no education? Is it really hopeless for specialist testers to enter Internet factories?
Getaverse,走向Web3的远方桥梁
Week 6 Learning Representation: Word Embedding (symbolic →numeric)
pillow的原因ImportError: cannot import name ‘PILLOW_VERSION‘ from ‘PIL‘,如何安装pillow<7.0.0
NetCore MySql The user specified as a definer (‘admin‘@‘%‘) does not exist
提升命令行效率的 Bash 快捷键 [完整版]
新人如何做好功能测试,学会这几项够用了
Okaleido launched the fusion mining mode, which is the only way for Oka to verify the current output
黑吃黑?男子破解赌博网站漏洞,每月“薅羊毛”10多万元
Uniapp applet framework - a set of code, multi segment coverage