java对象数组与基本类型数组

对象数组和基本类型数组有什么不同?

最近在看一本书,书名叫《软件困局:为什么聪明的程序员会写出糟糕的代码》,作者在第四章提到了C语言的数组。由此我想的了Java的数组,并在脑海里衍生出了一个问题。那就是 对象数组基本数据数组 的区别。乍一看两者都是数组,能有什么区别?但仔细一想发现不对。我们都知道数组是支持按下标 随机访问 的,算法复杂度为O(1)。之所以有如此快的访问速度是因为数组的每个元素所占用的大小是固定的。这样我们只需要知道头结点的地址,就可以推算出任意一个元素的地址。

像java中的int(4字节),long(8字节)等基本数据类型它们所占用的空间是固定的,所以数组能够随机访问很正常。但对象则不一样,拿String举例,每一个String对象所占用的空间是不一样的。数组该如何分配空间,以支持随机访问呢?这个问题我思考了很久,难道对象数组有着与基本数据类型数组不一样的实现方式吗?经过在网络一番查找,我得到了大致想要的答案。

直接说结论,那就是基本数据类型数组存放的是基本类型的值,对象数组存的是句柄(指针)。每个句柄(指针)所占用的空间是相同的,所以支持随机访问, 只不过对象数组需要比基本数据类型多一次寻址 (最后一句话仅是个人理解,不一定准确)。

我首先找到的是一篇博客 https://blog.csdn.net/z_ssyy/article/details/104521222 ,根据他的排版,感觉也是从其他地方摘抄的,内容并不完全。其中有这么一句话“ 唯一的差别在于对象数组容纳的是句柄,而基本数据类型数组容纳的是具体的数值 ”。后续我也尝试对这句话进行取证,先是准备查Java编程思想,看看其中有没有对数组的解释,但由于纸质书在老家,微信读书电子版只能试读,所以放弃。后续查阅了Java核心技术,发现当中并没有对应的解释。于是只能从《Java语言规范》和《Java虚拟机规范》找答案。

我并没有找到确切描述两者不同的文字,但可以从部分描述中推断出一部分信息。首先在《Java语言规范》的第四章 https://docs.oracle.com/javase/specs/jls/se16/html/jls-4.html ,有这么一段话:

The values of a reference type are references to objects

引用类型的值是对对象的引用,通过这句话我们可以推导出对象数组中存放的就是对象的引用。通过这句话也发现了自己以前的一个误区。那就是以前以为变量指向的就是数据,实际上变量指向的是数据的引用地址,需要再次寻址才能拿到真正的数据。

A variable of a primitive type always holds a primitive value of that exact primitive type.

这句话说明了基本数据类型,指向的就是值,没有引用。

思考

基本数据类型和引用数据类型的本质差别是什么?

将数据映射到硬件上,其实所有数据都应该有着自己的地址。不管是基本数据类型,还是引用数据类型。以下均为个人理解:

如图:

基本数据类型实际上只占用一块空间,而引用数据类型需要占用两块空间(注意每块空间的大小是不一样的)。这样我们对基本数据类型的修改,实际上改的就是地址01里面的值。对引用数据类型修改就会有两种情况,一种改的是03的值,一种改的的02的引用。举一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public static void main(String[] args) {
ArrayList<String> arrays = new ArrayList<>();
arrays.add("10");
arrays.add("20");
arrays.add("30");
System.out.println("未修改:" + arrays);
updateVal(arrays);
System.out.println("修改值:" + arrays);
List<String> newArrays = updateRef(arrays);
System.out.println("修改引用:" + arrays);
System.out.println("新引用值:" + newArrays);
}

/**
* 修改的是03地址当中的值
*
* @param arrays 原集合
*/
private static void updateVal(List<String> arrays) {
arrays.set(1, "1");
}

/**
* 修改的是02地址中的值,将02地址中的引用指向了04
*
* @param arrays 原集合
* @return 新集合
*/
private static List<String> updateRef(List<String> arrays) {
List<String> newArrays = new ArrayList<>();
newArrays.add("hello");
arrays = newArrays;
arrays.add("40");
arrays.add("50");
return arrays;
}