一、前言
数组引用变量只是一个引用,这个引用变量可以指向任何有效的内存,只有当该引用指向有效内存后,才可以通过该数组变量来访问数组元素.
定义并赋值一个数组时,要把数组看成两部分:
1.数组引用,也就是在代码中定义的数组引用变量;
2.实际的数组对象,这部分是在堆内存里运行的,通常无法直接访问它,只能通过数组引用变量来访问.
二、数组的初始化
两种方式
-
动态初始化
数据类型[ ] 数组名 = new 数据类型[长度];
eg : int[ ] arr = new int[10];拆分形式 : int[ ] arr ; arr = new int[10];
-
静态初始化
两种形式 :数据类型[ ] 数组名 = new 数据类型[ ] {元素1, 元素2, 元素3, ...};
eg : int[ ] arr = new int[ ]{1, 2, 3, 4};拆分形式 : int[ ] arr; arr = new int[ ]{1, 2, 3, 4};
数据类型[ ] 数组名 = {元素1, 元素2, 元素3, ...};
(此为上面的简略形式) 不允许拆分
eg : int[ ] arr = {1, 2, 3, 4};
三、深入数组
3.1 基本类型数组的初始化
初始化数组时,先为该数组分配内存空间,然后直接将数组元素的值存入对应数组元素中.
public class BasicArrayTest {
public static void main(String[] args) {
// 定义一个int[]类型的数组变量
int[] arr;
// 动态初始化数组,数组长度为5
arr = new int[5];
// 采用循环方式为每个数组元素赋值
for(int i = 0; i < arr.length; i++) {
arr[i] = i;
}
}
}
- 执行
int[] arr
时,仅定义一个数组变量,此时内存中的存储示意图如下:
仅在栈内存中定义了一个空引用(就是arr数组变量),这个引用并未指向任何有效的内存,故在初始化时无法指定数组的长度, - 执行
arr = new int[5];
动态初始化后,系统将负责将为该数组分配内存空间,并分配默认的初始值:所有数组元素都被赋值为0,此时内存中的存储示意图如下图:
- 执行循环为该数组的每个数组元素依次赋值后,此时每个数组元素的值都变成程序显示指定的值,显示指定每个数组元素值后的存储示意图如下:
- 每个数组元素的值直接存储在对应的内存中.操作基本类型数组的数组元素时,实际上就是操作基本类型的变量.
3.2 引用类型数组的初始化
引用类型数组的数组元素是引用,因此更复杂些.每个数组元素里存储的还是引用,它指向另一块内存,这块内存里存储了有效数据.
定义一个Person类(所有类都是引用类型):
class Person {
public int age;
public double height;
public void info() {
System.out.println("My age is: " + age + ", My height is: " + height);
}
}
定义一个Person[]数组,接着动态初始化这个Person[]数组,并未这个数组的每个数组元素指定值.
public class ReferenceArrayTest {
public static void main(String[] args) {
// 定义一个students数组变量,其类型是Person[]
Person[] students;
// 执行动态初始化
students = new Person[2];
// 创建一个Person实例,并将这个Person实例赋给zhang变量
Person zhang = new Person();
// 为zhang所引用的Person对象的age,height赋值
zhang.age = 15;
zhang.height = 175;
// 创建一个Person实例,并将这个Person实例赋给li变量
Person li = new Person();
// 为li所引用的Person对象的age,height赋值
li.age = 18;
li.height = 183;
// 将zhang变量的值赋给第一个数组元素
student[0] = zhang;
// 将li变量的值赋给第二个数组元素
students[1] = li;
// 下面两行代码的结果完全一样,因为li和students[1]指向的是同一个Person实例
li.info();
students[1].info();
}
}
- 执行
Person[] students;
,这行代码仅在栈内存中定义了一个引用变量,也就是一个指针,这个指针并未指向任何有效的内存区.此时内存中的存储示意图如下:
它仅仅是一个引用,并未指向任何有效的内存. - 执行动态初始化时,
students = new Person[2];
动态初始化由系统为数组元素分配默认的初始值:null,即每个数组元素的值都是null.执行动态初始化后的存储示意图如下图:
每个数组元素的值都是null.这意味着依然不能直接使用students数组元素,因为每个数组元素都是null,顶一个两个连续的Person变量,但这个变量还未指向任何有效的内存区. - 接着定义了
zhang和li两个Person实例
,定义这两个实例实际上分配了4块内存,在栈内存中存储了zhang和li两个引用变量,还在堆内存中存储了两个Person实例.此时内存存储示意图如下:
将zhang赋给students数组的第一个元素,把li赋给students数组的第二个元素
,students数组的两个数组元素将会指向有效的内存区.此时的内存存储示意图如下:
- 此时zhang和students[0]指向同一个内存区,而且它们都是引用类型变量,因此通过zhang和students[0]来访问Person实例的Field和方法的效果完全一样,不论修改students[0]还是zhang变量所指向的Person实例的Field,所修改的其实是同一个内存区,所以必然相互影响.
参考文献:<疯狂java讲义精粹_李刚编著>