您的位置:首页 > 编程语言 > Java开发

(1)疯狂java:数据与内存的控制____数组初始化__1.1.2 数组一定要初始化吗

2015-03-14 16:41 405 查看
          学习过<<疯狂java讲义>>(笔者另一部作品)读者一定还记得:使用java数组之前必须先初始化数组,也就是为数组元素分配内存空间,并指定初始值,实际上,如果真正掌握了java数组在内存中分配机制,那么完全可以换一个方式来初始化数组,或者说,数组无需经过初始化。

      初始记住:java的数组变量是引用类型的变量,它并不是数组对象本身,只要让数组对象指向有效的数组对象,程序即可使用该数组变量。示例如下是。

      程序清单:

      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数组的第3个元素,将看到输出34.
System.out.println("nums数组的第3个元素的值是:" + nums[2]);
}

   }

    从上面粗体字代码可以看出,程序定义了prices数组之后,并未对prices数组进行初始化。当执行int[] prices之后,程序的内存分配如图1.3所示。

        


         从图1.3可以看出,此时的prices数组变量还未指向任何有效的内存,未指向任何数组对象,此时的程序还不可使用prices数组变量。

         当程序执行prices = nums; 之后,prices变量将指向nums变量所引用的数组,此时prices变量和nums变量引用同一个数组对象,执行这条语句之后,prices变量已经指向有效的内存及一个长度为4的数组对象,因此程序完成可以正常使用prices变量了.

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

1.1.3  基本类型数组的初始化

       对于基本类型数组而言,数组元素的值直接存储在对应的数组元素中,因此基本类型的初始化比较简单:程序直接先为数组分配内存空间,再将数组元素的值存入对应内存里。

     下面程序采用静态初始化的方式初始化了一个基本类型的数组对象

       public class PrimitiveArrayTest

{
public static void main(String[] args) 
{
//定义一个int[]类型的数组变量
int[] iArr;
//静态初始化数组,数组长度为4
iArr = new int[]{2 , 5 , -12 , 20};
}

}

      上面代码的执行过程代表了基本类型数组初始化的典型过程,下面将结合示意图详细介绍这段代码的执行过程。

       执行第一行代码int[] iArr;时,仅定义一个数组变量,此时内存中的存储如图1.4所示。

       执行了int[] iArr;代码后,仅在main方法栈中定义了一个iArr数组变量,它是一个引用类型的变量,并未指向任何有效的内存,没有真正指向实际的数组对象,此时还不能使用该数组对象。

        当执行了iArr = new int[]{2,5,-12,20};静态初始化后,系统会根据程序员指定的数组元素来决定数组的长度,此时指定了4个数组元素,系统将创建一个长度为4的数组对象,一旦该数组对象创建成功,该数组的长度将不可改变,程序只能改变数组元素的值。此时内存中的存储如图1.5所示:

   


       静态初始化完成后,iArr数组变量所引用的数组所占用的内存空间被固定下来,程序员只能改变各数组元素内的值,但不能移动该数组所占用的内存空间---既不能扩大该数组对象所占用的内存,也不能缩减该数组对象所占用的内存。

        提示:对于程序运行过程中的变量,可以将它们形容为具体的瓶子---瓶子可以存储水,而变量也用于存储值,也就是数据,对于强类型语言如java,它有一个要求---怎样的瓶子只能将怎样的水,也就是说,指定类型的变量只能存储指定类型的值。

        有些书籍中总是不断地重复:基本类型变量的值存储在栈内存中,其实这句话是完全错误的。例如图1.5中的2、5、-12、20,它们都是基本类型的值,但实际上它们却存储在堆内存中,实际上应该说:所有局部变量都是放在栈内存里面保存的,不管其是基本类型的变量,还是引用类型的变量,都是存储在各自的方法栈区中,但引用类型变量所引用的对象(包括数组、普通java对象)则总是存储中堆内存中。

      对于java语言而言,堆内存中的对象(不管是数组对象,还是普通的java对象)通常不允许直接访问,为了访问堆内存中的对象,通常只能通过引用变量。这也是很容易混淆的地方,例如,iArr本质上只是main栈区的引用变量,但使用iArr.lenght,iArr[2]时,系统将会自动变为访问堆内存中的数组对象

     对于很多java程序员而言,他们最容易混淆的是:引用类型变量何时只是栈内存中的变量本身,何时又变为引用实际的java对象,其实规则很简单:引用变量本质上只是一个指针,只要程序通过引用变量访问属性,或者通过引用变量来调用方法,该引用变量将会由它引用的对象代替。

     示例如下。

     public class PrimitiveArrayTest2
{
public static void main(String[] args) 
{
//定义一个int[]类型的数组变量
int[] iArr = null;

             //只要不访问iArr的属性和方法,程序完全可以使用该数组变量
System.out.println(iArr);
//动态初始化数组,数组长度为5
iArr = new int[5];
//只有当iArr指向有效的数组对象后,下面才可访问iArr的属性
System.out.println(iArr.length);
}
}

     上面程序中2行粗体字代码两次访问iArr变量。对于1行代码而言,虽然此时的iArr数组变量并未引用到有效的数组对象,但程序在1处并不会出现任何问题,因为此时并未通过iArr访问属性或者调用方法,所以系统不会去访问iArr所引用的数组对象,对于2行代码而言,此时程序通过iArr访问length属性,程序将自动变为访问iArr所引用的数组对象,这就要求iArr必须引用一个有效的对象。

         注意:如果读者有一些编程经验,应该经常看到一个runtime异常:nullPointerException(空指针异常)。当通过引用变量来访问实例属性,或者调用非静态方法时,如果该引用变量还未引用一个有效的对象,程序就会引发nullPointerException运行时异常.

1.1.4  引用类型数组的初始化

     引用类型数组的数组元素依然是引用类型的,因此数组元素里存储的还是引用,它指向另一块内存,这块内存时存储了该引用变量所引用的对象(包括数组和java对象)

      为了说明引用类型数组的运行过程,下面程序先定义一个Person类,然后定义一个Person[]数组,并动态初始化了该Person[]数组,再显示为数组的不同数组元素指定值,该程序代码如下。

     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[]
Person[] students;
//执行动态初始化
students = new Person[2];
System.out.println("students所引用的数组的长度是:" + students.length);
//创建一个Person实例,并将这个Person实例赋给zhang变量
Person zhang = new Person();
//为zhang所引用的Person对象的属性赋值
zhang.age = 15;
zhang.height = 158;
//创建一个Person实例,并将这个Person实例赋给lee变量
Person lee = new Person();
//为lee所引用的Person对象的属性赋值
lee.age = 16;
lee.height = 161;
//将zhang变量的值赋给第一个数组元素
students[0] = zhang;
//将lee变量的值赋给第二个数组元素
students[1] = lee;
//下面两行代码的结果完全一样,因为lee和students[1]指向的是同一个Person实例。
lee.info();
students[1].info();
}

}

      上面代码的执行过程代表了引用类型数组的初始化的典型过程,下面将结合示意图详细介绍这段代码的执行过程。

      执行Person[] students; 代码时,这行代码仅仅在栈内存中定义了一个引用变量,也就是一个指针,这个指针并未指向任何有效的内存区,此时内存中的存储如图1-6所示:

       


      在图1.6中的栈内存中定义了一个students变量,它仅仅是一个空引用,并未指向任何有效的内存,直到执行初始化,本程序对students数组执行动态初始化,动态初始化由系统为数组元素分配默认的初始值null,即每个数组元素的值都是null,执行动态初始化后的存储如图1.7所示。

     


          
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: