一、数组介绍
数组属于Java中三大引用数据类型之一,其余两个为接口和类。数组在java中为相同数据类型的集合,在内存中存储于连续空间,并且存储于堆中。
数组定长:数组的长度一旦定义之后就是固定的长度不再改变。
两个语法:
静态初始化(直接给定数组内所有的元素是什么):
数据类型[ ]数组名称 ={元素1,元素2……元素N}; //数组长度编译器会根据元素的个事来自动计算并定长
例1:int [ ]num1 = {1,2 };
动态初始化(并未直接给定数组内所有元素是什么):
数据类型[ ]数组名称 = new数据类型[ 数组长度 ]; //数组长度已经给了,因为没有给定数组内元素,但是又要在堆中开辟空间,所以用的是其数据类型的默认值。该例子中为int的默认值
特别注意的:所有的引用数据类型的变量保存的只是一块内存的地址,并非具体数值,之所以叫引用数据类型,只是给真正的存储对象保存了一个地址而已。(可以理解为你在一个小房间里存了你的宝贝,基础数据类型就是代表你这个宝贝存的到底是什么,是吃的还是玩的,是指具体的这个实物,而引用数据类型只能算是一个门牌号,告诉你这个存的东西在哪里)。另外,只要在java中看见new 就为在堆中开辟一块新的空间进行存储,不管new了什么都是如此,但是静态初始化的语法和例子1中都没有new也在栈中开辟了新的空间是因为这是一个Java的语法糖,简化了步骤,实际上在经过编译之后仍然会产生new字节再执行。
例2:int [ ]num2 = new int [ 3 ];
tips:java中是无法取地址的,因此引用数据类型存储在栈中的数据也就是所谓的门牌号,我们无法得知。
图示如下:
例1:int [ ]num1 = {1,2 };
例2:int [ ]num2 = new int [ 3 ];
二、数组的基本使用
访问:数组使用时的实质是指,当使用该引用数据类型的时候,会在内存空间的堆中开辟一个新的空间用来存储实际的对象,这个实际存储的对象就是数组的元素,数组的元素在内存空间是连续存储的,每个数组元素都有编号。而数组会将在堆中存储的这些数组元素的第一个元素的地址存储下来,放进栈中,这样后续调用的时候就可以直接根据栈中存储的数组的地址信息,来到堆中读取数组中的各个元素。
特别的,数组中第一个数组元素的编号为0,为什么不是1而是0,是因为数组元素的编号代表着该元素对首位元素的偏移量,因为存储在栈中的是第一个数组元素的地址,那么第一个数组元素的编号为0就代表跟第一位的偏移量为0,那么它就是第一位,第二位的偏移量就为1,往后依次类推。
使用上面的例1
如图:
根据上文中编号的定义可以知道,当数组中存在n个元素时,编号最多编号到n-1,当超过n-1时,所得到的地址在栈中已经超过了该数组的保存范围,就会产生数组下标越界问题,显示的错误会如下图所示
错误1
其中最后的数字5表示,该编号5所提供的地址指向的栈中已经超过了数组范围。
几个知识点:
1 数组名称+ .length可以取得数组的长度 例1中 num1.length = 2
2 可以通过for循环来遍历数组的每一个元素,可以对其中的元素进行更改
如图
此处的i为编码,因此可以通过编码来更改编码对应的值,影响原数组
3 如果仅仅只是遍历数组的每一个元素的话,使用 for - each循环
此处的i不再是编码,而是一个局部变量,它用来接收拷贝过来的i的编码值(相当于新生成了一个栈帧),如果对此进行更改,不影响编码对应的实际的栈中的具体元素数据,不影响原数组。
4 交换swap方法为何能使用数组交换元素?
如下图
5 引用数据类型默认值null的问题
当引用数据类型的保存值为null时,此时该引用不指向任何栈中的内存,可以理解为空门牌号,空指针,无法进行任何具体操作。一旦操作则会出现NPE异常。
错误2
拿着引用为null的值进行了具体操作。
6 数组作为方法的返回值
返回的是一个集合,此处用斐波那契数列举例,如果用int做返回值,则返回对应数字的斐波那契数,而下图用数组返回了从第一项到第N项的斐波那契数。