Java进阶——数组与内存控制
下面将会深入探讨Java数组的静态特征。使用Java数组前必须对数组进行初始化,初始化的过程即给数组的所有元素都分配合适的内存空间,并指定初始值的过程。数组初始化以后将不能重新改变数组对象在内存中的位置和大小。从用法角度看,数组元素相当于普通变量,程序既可以吧数组的值赋给普通变量,也可以把普通变量的值赋给数组元素。但其实质是对内存中数组元素对象的操作。
1、数组初始化
Java语言是典型的静态语言,所以Java的数组也是静态的。Java中的数组必须经初始化后才可以使用,初始化即为数组对象的所有元素分配一个连续的内存空间,并为每个元素指定初始值。数组的初始化有两种方式:
>静态初始化:由程序员显式地指定每个数组元素的初始值,由系统决定数组的长度;
>动态初始化:程序员只指定数组长度,由系统为数组元素分配初始值。
下面是两种初始化方式的实例:
public class test01 {
public static void main(String[] args) {
//静态初始化数组
String[] str0 = new String[]{"小武灵灵" , "41009160"};
//静态初始化的简化形式
String[] str1 = {"小武灵灵" , "41009160"};
//动态初始化
String[] str2 = new String[2];
}
}
Java语言的数组变量是引用类型的变量,上面的三个数组变量按照声明的前后顺序(注意这里不是初始化的先后顺序)存储在main栈区中,其引用的数组是按照数组元素的顺序分别在堆内存中分配到长度为2的连续空间。
注意,虽然这里str0和str1变量引用的数组内容相同,但是绝不是同一个数组,也就是说内存中除了str2外,还有两个长度内容都相同的数组。
注意,鼎泰初始化和动态初始化不能同时使用。
而执行动态初始化时,程序员指定了数组的长度,即为每个数组元素指定所需的内存空间,系统负责为这些数组元素分配初始值。系统将按照如下规则分配到初始值:
>基本类型:
>整数类型(byte , short , int , long):数组元素的初始值为0;
>浮点类型(float , double):0.0;
>字符类型(char , string):’\u000’;
>布尔型(boolean):false;
>引用类型(类、接口和数组):null
1)、数组变量和数组对象
Java的数组变量是一种引用类型的变量,通常情况下被存放在栈内存中,它并不是数组本身,它只是指向堆内存中的数组对象(跟C语言里的指针差不多)。
数组对象是保存在堆内存中的连续内存空间。
理解了这两个概念之后,我们可以对数组的初始化有一个更深的认识,即对数组执行初始化,其实并不是对数组变量执行初始化,而是要对数组对象执行初始化(即为该数组对象分配一块连续的内存空间)。
认识到这一点之后我们可以解释这样一种假象,即认为数组的长度是可变的,例如下面的实例:
public class test01 {
public static void main(String[] args) {
String[] str0 = new String[]{"小武灵灵" , "41009160" , "陕西师范大学"};
String[] str1 = new String[2];
str1 = str0;
System.out.println("str1的长度为:" + str0.length);
}
}
相信大家可以自行解释这种假象,这里我就不解释啦!
2)、基本类型数组的初始化
基本类型数组的初始化过程为:程序直接为数组分配内存空间,再将数组元素的值存入对应的内存里。例如下面的代码块:
String[] str;
str = new String[]{“小武灵灵” , “41009160”, “地信1班”};
注意,所有局部变量都是放在栈内存里保存的,不管其是基本类型的变量,还是引用类型的变量,都是存储在各自的方法栈区中;但引用类型变量所引用的变量(包括数组、普通Java对象)则总是存储在对内存中。
注意,引用变量本质上只是一个指针,只要程序通过引用变量访问属性,或通过引用变量来调用方法,该引用变量将会由它所引用的对象代替。
3)、引用类型数组的初始化
引用类型数组的数组元素依然是引用类型的,因此数组元素里存储的还是引用,它指向另一块内存,这块内存里存储了该引用变量所引用的对象。例如:
class Student {
public String name;
public int number;
public void output() {
System.out.println(name + “,”+number);
}
}
public class Main{
public static void main(String[] args) {
Student[] stu = new Student[2];
System.out.println(“数组长度:” + stu.length);
Student xiaowu = new Student();
xiaowu.name = “小武”;
xiaowu.number = “41009160”;
stu[0] = xiaowu;
//下面两行代码所输出的结果都是一样的
stu[0].output();
xiaowu.output();
}
}
在上面的程序中,内存是这样分配的:首先引用数组变量stu[]初始化完成后,内存中开辟出了长度为二的空间用来存储student对象;对引用变量xiaowu初始化完成后,内存中开辟了出了能够存储一个student对象长度的空间,用来存储student对象;将xiaowu赋给stu[0],则在内存中数组stu[0]指向对象xiaowu。
2、数组的使用
当数组引用变量指向一个有效的数组对象之后,程序就可以通过该数组引用变量来访问数组对象。Java语言不允许直接访问对内存中的数据,因此无法直接访问堆内存中的数组对象,程序将通过数组引用变量来访问数据。
要对数组有一个更深刻的认识,需要理解两个关系:数组元素与变量间的关系、多维数组与变量间的关系。
1)、数组元素与变量间的关系
只要在已有数据类型之后增加括号,就会产生一个新的数组类型,例如:
>int à int[]:在int类型后增加[]即变为int[]数组类型
>Stirng à String[]:在String类型后增加[]即变为String[]数组类型
当程序需要多个类型相同的变量来保存程序状态时,可以考虑使用数组来保存这些变量。当一个数组初始化完成,就相当于定义了多个类型相同的变量。
无论哪种类型的数组,其数组元素其实相当于一个普通变量,把数组类型之后的括号去掉之后得到的类型就是该数组元素的类型。
从这个角度讲,数组元素就是变量。
注意,这里的数组元素并不是指数组对象中的数组元素,而是指数组变量的数组变量元素。
注意,main方法声明的变量都属于局部变量,因此它们都被保存在main方法栈中;但数组元素则作为数组对象的一部分,总是保存在堆内存中,不管它们是基本类型的数组元素,还是引用类型的数组元素。
2)、多维数组与变量间的关系
理解了数组元素与变量之间关系之后,如果已有的类型是int,在其后增加[]后得到一个数组类型;以int[]类型为已有类型,在其后增加[]后得到int[][]依然是数组类型;
以int[][]类型为已有类型,在其后增加[]后得到int[][][]依然是数组类型。
从上面可以看出,所谓多维数组,其实就是数组元素依然是数组的1维数组,2维数组是数组元素是1维数组的数组,3维数组是数组元素是2维数组的数组,4维数组是数组元素是3维数组的数组……N维数组是数组元素是N-1维数组的数组。
所以,实际上我们可以这样认为:没有多维数组,只有基本数据类型和引用数据类型。
综上,数组的初始化有静态初始化和动态初始化两种;数组对象和数组变量不是同一个概念对于数组变量而言;数组元素的类型分为基本类型的数组和引用类型的数组;所有局部变量都是存放在栈内存里保存的,但引用类型变量所引用的对象则总是存储在堆内存中;一定要区分它何时只是数组变量,何时代表数组对象本身;数组元素就是变量,没有多维数组。