数组与内存控制:
1.1数组初始化
java 语言的数组变量是引用类型的变量 。
1.1.1java数组是静态的
java是静态语言,java数组是静态的,即数组被初始化后数组的长度是不可变的,java程序中的数组必须经过初始化后才可以使用。初始化过程:为数组对象的元素分配内在空间,并为每个数组元素指定初始值。
数组初始化的两种方式:
静态初始化:初始化时由程序员显式指定每个数组元素的初始值,由系统决定数组长度。
动态初始化:初始化时程序员只指定数组长度,由系统为数组元素分配初始值。
不管用哪种初始化方式,java数组一旦初始化完成,该数组的长度就不可改变。
例:
package arrayAndMemoryControl; public class ArrayTest { public static void main(String args[]){ //采用静态初始化方式初始化第一个数组 String [] books = new String[]{"深入理解JVM","深入理解操作系统","算法导论","java编程思想"}; //采用静态初始化的简化形式初始化第二个数组 String names[] = {"James","Rick","Jun"}; //采用动态初始化的方式初始化第三个数组 String []strArr = new String[5]; //访问3个数组长度 System.out.println("第一个数组长度:" + books.length); System.out.println("第二个数组长度:" + names.length); System.out.println("第三个数组长度:" + strArr.length); } }
执行动态初始化时:
数组元素类型是整数类型(byte,short,int,long),则数组元素的值是0
数组元素类型是浮点类型(float,double),则数组元素的值是0.0
数组元素类型是字符类型(char),则数组元素的值是 '\u0000'
数组元素类型是布尔类型(boolean),则数组元素的值是false
数组元素类型是引用类型(类,接口,数组),则数组元素的值是null
注:改变一个数组变量所引用的数组,可以造成数组长度可变的假象。java的数组变量是一种引用类型的变量 ,数组变量并不是数组本身,它只是指向堆内存中的数组对象
例:
package arrayAndMemoryControl; public class ArrayTest2 { public static void main(String args[]){ //采用静态初始化方式初始化第一个数组 String [] books = new String[]{"深入理解JVM","深入理解操作系统","算法导论","java编程思想"}; //采用静态初始化的简化形式初始化第二个数组 String names[] = {"James","Rick","Jun"}; //采用动态初始化的方式初始化第三个数组 String []strArr = new String[5]; //让books数组变量和strArr数组变量指向names所引用的数组 books = names; strArr = names; //访问3个数组长度 System.out.println("books数组长度:" + books.length); System.out.println("strArr数组长度:" + strArr.length); //改变books数组变量所引用的数组的第二个元素值 books[1] = "设计模式"; System.out.println("names数组的第二个元素:" + books[1]); } }
1.1.2注:Java的数组变量是引用类型的变量,它并不是数组对象本身,只要让数组变量指向有效的数组对象,程序中就可以使用该数组变量。
例:
package arrayAndMemoryControl; public class ArrayTest3 { public static void main(String args[]){ //定义并初始化nums数组 int [] nums = new int []{3, 5, 20, 12}; //定义一个prices数组变量 int [] prices; //让prices数组指向nums所引用的数据 prices = nums; for (int i = 0; i < prices.length; i++){ System.out.println(prices[i]); } //将prices数组的第3个元素赋值为34 prices[2] = 34; //访问nums数组的第三个元素 System.out.println("nums数组的第3个元素的值是:" + nums[2]); } }
1.1.3 基本类型数组的初始化:程序直接先为数组分配内存空间,再将数组元素的值存入对应的内存里。
例:用静态初始化的方式初始化一个基本类型的数组对象
package arrayAndMemoryControl; public class ArrayTest4 { public static void main(String args[]){ //定义一个int[]类型的数组变量,这是一个引用类型变量,并未指向任何有效内存, int [] iArr; //静态初始化数组,数组长度为4 iArr = new int[]{2, 5, -12, 30}; } }
注: 如2,-5,23,33都是基本类型的值,都存储在堆内存中。所有局部变量都是放在栈内存里保存的,不管是基本类型的变量还是引用类型的变量 ,都是存储在各自的方法区栈中;但引用类型变量所引用的对象(包括数组和普通java对象)则总是存放在堆内存中。
堆内存中的对象通常不允许直接访问,为了访问堆的中对象,通常只通过引用变量。引用变量本质上只是一个指针,只要程序通过引用变量访问属性,或者通过引用变量调用方法,该引用变量将会由它所引用的对象代替。
例:
package arrayAndMemoryControl; public class PrimitiveArrayTest { public static void main(String args[]){ //定义一个int[] 类型的数组变量 int [] iArr = null; //只要不访问iArr的属性和方法,程序完全可以使用该数组变量 System.out.println(iArr); //动态初始化数组长度为5 iArr = new int[5];// //只有当iArr指向有效的数组对象后,下面才可以访问iArr的属性,如果iArr未初始化,运行将报空指针错误 System.out.println(iArr.length); //注:当通过引用变量访问实例属性或调用非静态方法时,如果该引用变量还未引用一个有效的对象,程序会出现NullPointerException运行时异常 } }
1.1.4引用类型数组的初始化
引用类型数组的数组元素依然是引用类型的,因此数组元素里存储的还是引用变量,它指向另一块内存,这块内存里存储了该引用变量所引用的对象(包括数组和java对象)
例:
package arrayAndMemoryControl; class Person { public int age;//年龄 public double height;//身高 //定义一个info方法 public void info(){ System.out.println("年龄:" + age + ", 身高:" + height); } } public class ReferenceArrayTest{ public static void main(String args[]){ //定义一个students数组变量 Person [] students; //执行动态初始化 students = new Person[2]; System.out.println("students所引用的数组长度是:" + students.length); //创建一个实例并将Person实例赋值给xie变量 Person xie = new Person(); //为xie所引用的person对象的属性赋值 xie.age = 12; xie.height = 200; //创建一个Person实例赋值给zhong Person zhong = new Person(); //为zhong所引用的person对象的属性赋值 zhong.age = 2; zhong.height = 201; //将xie变量的值赋给第一个数组元素 students[0] = xie; //将zhong变量的值赋给第二个数组元素 students[1] = zhong; //下面两行代码的结果完全一样,因为zhong和students[1]指向的是同一个 person实例 zhong.info(); students[1].info(); } }
1.2 使用数组
当数组引用变量指向一个有效数组对象后,程序可通过该数组引用变量来访问数组对象。java不允许直接访问堆内存中的数据,因此无法直接访问堆内在中的数组对象,程序将通过数组引用变量来访问数组。
1.2.1数组元素就是变量
例:数组元素和普通变量相互赋值 package arrayAndMemoryControl; class Cat { double weight; int age; public Cat(double weight, int age){ this.weight = weight; this.age = age; } } public class ArrayTest5 { public static void main(String args[]){ //定义并动态初始化一个int[]数组 int []pos = new int[5]; //循环为每个元素赋值 for(int i = 0; i < pos.length; i++){ pos[i] = (i + 1) * 2; } //对于pos元素来说,用起来完全等同于普通变量 //下面既可将数组元素的值赋给int变量,也可将int变量的值赋给数组元素 int a = pos[1]; int b = 20; pos[2] = b; //定义并动态初始化一个cat[]数组 Cat[] cats = new Cat[2]; cats[0] = new Cat(3.34, 2); cats[1] = new Cat(3.2, 2); //将cats数组的第一个元素值赋给c1 Cat c1 = cats[0]; Cat c2 = new Cat(4.3, 3); //将c2的值赋给cats数组的第二个元素 cats[1] = c2; } }
注:main方法声明的变量都属于局部变量,因此它们都被保存在main方法的栈中;但数组元素作为数组对象的一部分,总是保存在堆内存中,不管它们是基本类型的数组元素还是引用类型的数组元素。
1.2.2没有多维数组:N维数组是数组元素N-1维数组的数组,多维数组的本质是一维数组。
package arrayAndMemoryControl; public class TwoDimensionTest { public static void main(String args[]){ //定义一个2维数组 int [][] a; //把a当成一维数组进行初始化为长度4的数组 //a数组的数组元素又是引用类型 a = new int[4][]; //把a数组当成一维数组遍历a数组的每个元素 for(int i = 0; i < a.length; i++){ System.out.println(a[i]); } //初始化a数组的第一个元素 a[0] = new int[2]; //访问a数组的第一个元素所指数组的第2个元素 a[0][1] = 6; //a数组的第一个元素是1个一维数组,遍历这个一维数组 for(int i = 0; i < a[0].length; i++){ System.out.println(a[0][i]); } } }
如果定义一个Object[]类型的数组,每个数组元素都相当于一个Object类型的引用变量 ,因此可以指向任何对象(包括数组对象和普通java 对象),
例:极端程序理解数组在内存中的分配机制
package arrayAndMemoryControl; public class ObjectArrayTest { public static void main(String args[]){ //定义并初始化一个Object数组 Object []objArr = new Object[3]; //让objArr所引用数组的第2个元素再次指向一个长度为2的Object[]数组 objArr[1] = new Object[2]; //让objArr[2]和objArr[1]指向同一个数组对象 Object[] objArr2 = (Object[])objArr[1]; //让objArr2所引用数组的第2个元素再次指向一个长度为3的object[]数组 objArr2[1] = new Object[3]; //让objArr3和objArr2[1]指向同一个数组对象 Object[] objArr3 = (Object[])objArr2[1]; //让objArr2所引用的数组的第2个元素再次指向一个长度为5的int[]数组 objArr3[1] = new int[5]; //将objArr3[1]的值赋给iArr,即让iArr和objArr3[1]指向同一个数组对象 int [] iArr = (int[])objArr3[1]; //依次为iArr数组的每个元素赋值 for(int i = 0; i < iArr.length; i++){ iArr[i]= i * 3 + 1; } //直接通过objArr访问iArr数组的第三个元素 System.out.println(((int[])((Object[])((Object[])objArr[1])[1])[1])[2]); } }
总结:Java数组一旦初始化完成,该数组长度将不可改变。