第1课—数组与内存控制

引言

JAVA数组并不是什么很难的知识,如果单从用法的角度来看,数组的用法并不难,只是很多程序员虽然一直使用JAVA数组,但他们往往对JAVA数组的内存分配把握并不准确。本章正是为了弥补程序员的这部分基本功而做的深入探讨

本课将会深入探讨JAVA数组的静态特征。使用JAVA数组之前必须先对数组对象进行初始化。当数组的所有元素都被分配了合适的内存空间,并指定了初始值时,数组初始化完成。程序以后将不能重新改变数组对象在内存中的位置和大小。
从用法角度来看,数组元素相当于普通变量,程序即可把数组元素的值赋给普通变量,也可以把普通变量的值赋给数组元素。
f本课还将深入分析多维数组的实质,深入理解多维数组和一位数组之间的关联,并通过程序示范如何将一位数组扩展成多维数组。

1.1 数组初始化

数组是大多数编程语言都提供的一种复合结构,如果程序需要多个类型相同的变量时,就可以考虑定义一个数组。Java语言的数组变量是引用类型的变量,因此具有独有的特性

1.1.1 Java数组是静态的

Java语言是典型的静态语言,因此Java的数组是静态的,即当数组被初始化之后,该数组的长度是不可变的。Java程序中的数组必须经过初始化后才可以使用。所谓初始化,就是维数组对象的元素分配内存空间,并为每个数组元素指定初始值。
数组的初始化有以下两种方式:

  • 静态初始化:
    初始化时由程序员显示指定每个数组元素的初始值,由系统决定数组长度
  • 动态初始化:
    初始化时程序员只指定数组长度,由系统为数组元素分配初始值。
程序清单: codes\01\1.1\ArrayTest.java
public class ArrayTest {

	public static void main(String[] args) {
		// 采用静态初始化方式初始化第1个数组
		String[] books = new String[] { "疯狂Java讲义", "轻量级Java EE企业应用实战", "疯狂Ajax讲义", "疯狂XML讲义" };
		// 采用静态初始化的简化形式初始化第2个数组
		String[] names = { "孙悟空", "猪八戒", "白骨精" };

		// 采用动态初始化的语法初始化第3个数组
		String[] strArr = new String[5];
		// 访问三个数组的长度
		System.out.println("第一个数组的长度: " + books.length);
		System.out.println("第二个数组的长度: " + names.length);
		System.out.println("第三个数组的长度: " + strArr.length);
	}

}

结果如下:

第一个数组的长度: 4
第二个数组的长度: 3
第三个数组的长度: 5

分析

上面的程序代码声明并初始化了3个数组。这3个数组的长度将会始终不变,程序输出3个数组的长度依次为3、4、5
在这里插入图片描述
从该图可以看出,对于静态初始化方式而言,程序员无需指定数组长度,指定该数组的数组元素,有系统来决定该数组的长度即可。例如,books数组,为它指定了4个数组元素,那它的长度就是4。
执行动态初始化时,程序员只需要指定数组的长度,即为每个数组元素指定所需的内存空间,系统将负责为这些数组元素分配初始值。指定初始值时,系统将按如下规则分配初始值。

  • 数组元素的类型是基本类型中的整数类型(byte , short , int 和long),则数组元素的值是0.
  • 数组元素的类型是基本类型中的浮点类型(float, double) , 则数组元素的值是 0.0.
  • 数组元素的类型是基本类型中的字符类型(char) , 则数组元素的值是 ‘\u0000’.
  • 数组元素的类型是基本类型中的布尔类型(boolean), 则数组元素的值是 false.
  • 数组元素的类型是引用类型(类、 接口、 和数组), 则数组元素的值是 Null。

注意:
不要同时使用静态初始化和动态初始化。也就是说,不要再进行数组初始化时,即指定数组的长度,也为每个数组元素分配初始值。
Java的数组是静态的,一旦为数组初始化完成,数组元素的内存空间分配即结束,程序只能改变数组元素的值,而无法改变数组的长度。
需要指出的是,Java的数组变量是一种引用类型的变量,数组变量本身并不是数组本身,它只是指向堆内存中的数组对象。因此,可以改变一个数组变量所引用的数组,这样就可以造成数组长度可变的假象。假设,在上面程序后增加几行。

代码是:
public class ArrayTest {

	public static void main(String[] args) {
		// 采用静态初始化方式初始化第1个数组
		String[] books = new String[] { "疯狂Java讲义", "轻量级Java EE企业应用实战", "疯狂Ajax讲义", "疯狂XML讲义" };
		// 采用静态初始化的简化形式初始化第2个数组
		String[] names = { "孙悟空", "猪八戒", "白骨精" };

		// 采用动态初始化的语法初始化第3个数组
		String[] strArr = new String[5];
		// 访问三个数组的长度
		System.out.println("第一个数组的长度: " + books.length);
		System.out.println("第二个数组的长度: " + names.length);
		System.out.println("第三个数组的长度: " + strArr.length);
		
		//codes\01\1.1\ArrayTest2.java
		//让books数组变量、strArr数组变量指向names所引用的数组
		books = names;
		strArr = names;
		System.out.println("----------------");
		System.out.println("books数组的长度:" + books.length);
		System.out.println("strArr数组的长度: " + strArr.length);
		//改变books 数组变量所引用的数组的第2 个元素值
		books[1] = "唐僧";
		System.out.println("names数组的第2个元素是:" + books[1]);
	}

}
运行结果为:

第一个数组的长度: 4
第二个数组的长度: 3
第三个数组的长度: 5


books数组的长度:3
strArr数组的长度: 3
names数组的第2个元素是:唐僧

解释:

上面程序中粗体字代码将让books数组变量、strArr数组变量都指向names数组变量所引用的数组,这样做的结
果就是books、 strArr 、 names 这3个变量引用同一个数组对象。此时,3个引用变量和数组对象在内存中的
分配如图所示。
从图中可以看出,此时strArr、 names、 和books数组变量实际上引用同一个数组对象。因此,当访问books数组长度、strArr数组长度时,将看到输出3、这很容易造成一个假象:books数组的长度从4变成了3.实际上,数组对象本身的长度并没有发生改变,变得是books数组变量。books数组变量原本指向堆内存下面的数组,当执行了books = names; 语句之后,books数组将改为指向堆内存中间的数组,而原来books变量所引用的数组长度依然是4.
当然,从图中还可以看出,原来books变量所引用的数组长度依然是4,但不再有任何引用变量引用该数组,因此它将会变成垃圾,等着垃圾回收机制来回收。此时,程序使用books、 names、 和 strArr这3个变量时,将会访问同一个数组对象,因此把books数组的第2个元素赋值为“唐僧”时,names 数组的第2个元素的值也会随之改变。

与 java这种静态语言不同的是,JavaScript这中动态语言的数组长度是可以动态改变的,示例如下:

上面是一个简单的JavaScript程序。它先定义了一个名为arr的空数组,因为它不包含任何数组元素,所以它的长度为0。接着,为arr数组的第3个、第5个元素赋值,该数组的长度也自动变为5.这就是JavaScript里的动态数组和Java里静态数组的区别

1.1.2 数组一定要初始化吗

使用Java数组之前必须先初始化数组,也就是为数组元素分配内存空间,并指定初始值。实际上,如果真正掌握了Java数组在内存中分配机制,那么完全可以换一个方式来初始化数组,或者说,数组无需经过初始化。
始终记住:Java的数组变量是引用类型的变量,它并不是数组对象本身,只要让数组变量指向有效的数组对象,程序中即可使用该数组变量。实例如下:

代码如下:

public class ArrayTest3 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//定义并初始化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数组的第3个元素,将看到输出34
		System.out.println("nums数组的第三个元素的值是: " + nums[2]);
	}

}
运行结果:

3
5
20
12
nums数组的第三个元素的值是: 34

从图中可以看出,此时的prices数组变量还未指向任何有效的内存,为指向任何数组对象,此时的程序还不可使用prices数组变量。
当程序执行prices = nums ;之后,prices变量将指向nums变量所引用的数组,此时prices变量和nums变量所引用同一个数组对象。执行这条语句之后,prices变量已经指向有效的内存及一个长度为4的数组对象,因此程序完全可以正常使用prices变量了。
注意:
常常说使用Java数组之前必须先进行初始化,可是现在prices变量却无需初始化,这不是互相矛盾吗?其实一点都不矛盾。关键是大部分时候,我们把数组变量和数组对象搞混了,数组变量知识一个引用变量(有点类似C语言里的指针),通常存放在栈内存中(也可以被放入堆内存中的);而数组对象就是保存在堆内存中的连续内存空间。对数组执行初始化,其实并不是对数组变量执行初始化,二是要对数组对象执行初始化——也就是为该数组对象分配一款连续的内存空间,这块连续的内存空间的长度就是数组的长度。虽然上面程序中的prices变量看似没有经过初始化,但执行prices = nums;就会让prices变量直接指向一个已经执行初始化的数组

对于数组变量来说,它并不需要进行所谓的初始化,只要让数组变量指向一个有效的数组对象,程序即可正常使用该数组变量

提示
对于Java程序中所有的引用变量,它们都不需要经过所谓的初始化操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值