营收|藏在Java数组的背后,你可能忽略的知识点( 二 )


数组是如何做到随机访问的?
事实上 , 数组的数据是按顺序存储在内存的连续空间内的 , 从上面的图我们看出来 , 即便Java二维数组是呈树形结构 , 但是各个一维数组的元素是连续的 , 通过arr[0
arr[1
等数组对象指向一维数组 , 所以每个数据的内存地址(在内存上的位置)都可以通过数组下标算出 , 我们也就可以借此直接访问目标数据 , 也就是随机访问
Java数组与内存上面这么说还是有点懵懵懂懂的 , 可以画图解看看Java 数组在内存中的存储是怎么样的?
数组对象(类比看作指针)存储在栈中 , 数组元素存储在堆中
一维数组:二维数组:
精彩点评:一维数组在堆上连续的内存空间直接存储值 , 二维数组在连续的地址上存储一维数组的引用地址 , 一维数组与一维数组并不一定靠在一起 , 但是这些一维数组内部的值是在连续地址上的 。 更高维的数组继续以此类推 , 只有最后一维数组在连续地址上保存值 , 其他纬度均在连续地址上保存下一维度的引用地址 。 同维度的实例不一定靠在一起 。
解惑数组下标为什么是从0开始?
前面说到数组访问数据时使用的是随机访问(通过下标可计算出内存地址) , 从数组存储的内存模型上来看 , “下标”最确切的定义应该是“偏移(offset)” 。 如果用 a 来表示数组的首地址 , a[0
就是偏移为 0 的位置 , 也就是首地址 , a[k
就表示偏移 k 个 type_size 的位置 , 所以计算 a[k
的内存地址只需要用这个公式:
a[k
_address = base_address + k * type_size

但是 , 如果数组从 1 开始计数 , 那我们计算数组元素 a[k
的内存地址就会变为:
a[k
_address = base_address + (k-1)*type_size

对比两个公式 , 可以发现 , 从 0 开始编号 , 每次随机访问数组元素都少了一次减法运算 , 对于 CPU 来说 , 就是少了一次减法指令 ,提高了访问的效率
数组的本质Java中的数组是对象吗?Java和C++都是面向对象的语言 。 在使用这些语言的时候 , 我们可以直接使用标准的类库 , 也可以使用组合和继承等面向对象的特性构建自己的类 , 并且根据自己构建的类创建对象 。 那么 , 我们是不是应该考虑这样一个问题:在面向对象的语言中 , 数组是对象吗?
判断数组是不是对象 , 那么首先明确什么是对象 , 也就是对象的定义 。 在较高的层面上 , 对象是根据某个类创建出来的一个实例 , 表示某类事物中一个具体的个体 。 对象具有各种属性 , 并且具有一些特定的行为 。 而在较低的层面上 , 站在计算机的角度 , 对象就是内存中的一个内存块 , 在这个内存块封装了一些数据 , 也就是类中定义的各个属性 , 所以 , 对象是用来封装数据的 。 以下为一个Person对象在内存中的表示:
注意:
1、红色矩形表示一个引用(地址)或一个基本类型的数据 , 绿色矩形表示一个对象 , 多个红色矩形组合在一块 , 可组成一个对象 。
2、name在对象中只表示一个引用 ,也就是一个地址值 , 它指向一个真实存在的字符串对象 。 在这里严格区分了引用和对象 。
那么在Java中 , 数组满足以上的条件吗?在较高的层面上 , 数组不是某类事物中的一个具体的个体 , 而是多个个体的集合 。 那么它应该不是对象 。 而在计算机的角度 , 数组也是一个内存块 , 也封装了一些数据 , 这样的话也可以称之为对象 。 以下是一个数组在内存中的表示:
这样的话 ,数组既可以是对象 ,也可以不是对象 。 至于到底是不是把数组当做对象 , 全凭Java的设计者决定 。 数组到底是不是对象 ,通过代码验证:
int[
arr = new int[4
;int len = arr.length;  //数组中保存一个字段 表示数组的长度//以下方法说明数组可以调用方法Java数组是对象.这些方法是Object中的方法所以可以肯定数组的最顶层父类也是Objectarr.clone();


推荐阅读