数组的初始化

在C语言中初始化数组既容易出错,又相当麻烦。C++ 则通过“聚合初始化(aggregate

initialization)”使其更安全7。在Java中,一切都是对象,也没有类似C++里“聚合”那样的

概念。但它确实提供了数组,也支持数组初始化。

 

数组只是相同类型的、用一个标识符名称封装到一起的一个对象序列或基本类型数据序列。

数组是通过方括号索引操作符[]来定义和使用的。要定义一个数组,只需在类型名后加上一

对空方括号即可:

 

int[] a1;

 

方括号也可以置于标识符后面,其含义相同:

 

int a1[];

 

这种格式符合C 和 C++程序员的习惯。不过,前一种格式或许更合理,毕竟它表明类型是

“一个 int型数组”。本书将采用这种格式。

 

编译器不允许你指定数组的大小。这就又把我们带回到有关“引用”的问题上。现在你拥有

的只是对数组的一个引用,而且也没给数组分配任何空间。为了给数组创建相应的存储空间,

你必须写初始化表达式。对于数组,初始化动作可以出现在代码的任何地方,但也可以使用

一种特殊的初始化表达式,它必须在创建数组的地方出现。这种特殊的初始化是由一对花括

号括起来的值组成的。在这种情况下,存储空间的分配(等价于使用 new)将由编译器负责。

例如:

 

int[] a1 = { 1, 2, 3, 4, 5 };

 

那么,为什么还要在没有数组的时候定义一个数组引用呢?

 

int[] a2;

 

在 Java 中你可以将一个数组赋值给另一个数组,所以你可以这样:

 

a2 = a1;

 

其实你真正做的只是复制了一个引用,就象下面演示的那样:

//:c04:Arrays.java

// Arrays ofprimitives.

import com.bruceeckel.simpletest.*;

 

public   class Arrays {

static Test monitor = new Test();

public   static     void main(String[] args) {

int[] a1 = { 1, 2, 3, 4, 5 };

int[] a2;

    a2 = a1;

for(int i = 0; i < a2.length; i++)

      a2[i]++;

for(int i = 0; i < a1.length; i++)

      System.out.println(

"a1[" + i + "] = " + a1[i]);

    monitor.expect(new String[] {

"a1[0] =2",

"a1[1] =3",

"a1[2] =4",

"a1[3] =5",

"a1[4] =6"

    });

  }

}   ///:~

你可以看到代码中给出了 a1 的初始值,但 a2却没有;在本例中,a2 是在后面被赋值为另

一个数组的。

这里有点新东西:所有数组(无论它们的元素是对象还是基本类型)都有一个固有成员,你

可以通过它获知数组内包含了多少个元素,但不能对其修改。这个成员就是 length。与 C和

C++类似,Java 数组计数也是从第 0 个元素开始,所以能使用的最大索引数是“length - 1”。

要是超出这个边界,C 和 C++会“默默”地接受,并允许你访问所有内存,许多声名狼藉的

程序错误由此而生。Java 则能保护你免受这一问题的困扰,一旦访问下标过界,就会出现运

行期错误(即“异常”,将在第 9 章中讨论)。当然,每次访问数组的时候都要检查边界的做

法是需要在时间和代码上有所开销的,但是你无法禁用这个功能。这意味着如果数组访问发

生在一些关键节点上,它们有可能会成为导致程序效率低下的原因之一。但是基于“因特网

的安全以及提高程序员生产力”的理由,Java的设计者认为这是值得的取舍。

 

如果在编写程序时,你并不能确定在数组里需要多少个元素,那么你该怎么办呢?你可以直

接用 new 在数组里创建元素。尽管创建的是基本类型数组,new 仍然可以工作(不能用 new

创建单个的基本类型数据)。

 

//:c04:ArrayNew.java

// Creatingarrays with new.

import com.bruceeckel.simpletest.*;

import java.util.*;

 

public   class ArrayNew {

static Test monitor = new Test();

static Random rand =    new Random();

public   static    void main(String[] args) {

int[] a;

    a =  new int[rand.nextInt(20)];

    System.out.println("length of a = " + a.length);

for(int i = 0; i < a.length; i++)

      System.out.println("a[" + i +         "] = " + a[i]);

    monitor.expect(new Object[] {

"%% lengthof a = \\d+",

new TestExpression("%% a\\[\\d+\\] = 0", a.length)

    });

  }

}   ///:~

本例中的expect( )语句里有些新内容:TestExpression 类。要创建 TestExpression对象需要传

入一个表达式,此表达式既可以是普通的字符串,也可以是如例子中的正则表达式;还要传

入一个整形参数用以说明表达式重复的次数。如本例所示,TestExpression       类不仅可以防止

无意义的代码重复,还能在运行时刻决定重复的次数。

 

数组的大小是通过Random.nextInt( )方法随机决定的,这个方法会返回 0到输入参数之间的

一个值。这表明数组的创建确实是在运行时刻进行的。此外,程序输出表明:数组元素中的

基本数据类型值会自动初始化成“空”值。(对于数字和字符,就是 0;对于布尔型,是 false)。

 

当然,数组也可以在定义的同时进行初始化:

int[] a = new     int[rand.nextInt(20)];

 

如果可能的话,你应该尽量这么做。

 

如果数组里的元素不是基本数据类型,那么你必须使用 new。在这里,你会再次遇到引用问

题,因为你创建的数组里每个元素都是一个引用。以整型的包装类             Integer(它可不是基本

类型)为例:

 

//:c04:ArrayClassObj.java

// Creating anarray of nonprimitive objects.

import com.bruceeckel.simpletest.*;

import java.util.*;

 

public   class ArrayClassObj {

static Test monitor = new Test();

static Random rand =    new Random();

public   static     void main(String[] args) {

    Integer[] a = new Integer[rand.nextInt(20)];

    System.out.println("length of a = " + a.length);

for(int i = 0; i < a.length; i++) {

      a[i] =    new Integer(rand.nextInt(500));

      System.out.println("a[" + i +         "] = " + a[i]);

    }

    monitor.expect(new Object[] {

"%% lengthof a = \\d+",

new TestExpression("%% a\\[\\d+\\] = \\d+", a.length)

    });

  }

}   ///:~

 

这里,即便使用new 创建数组之后:

 

Integer[] a = new Integer[rand.nextInt(20)];

 

它还只是一个引用数组,并且直到通过创建新的 Integer对象,并且把对象赋值给引用,初

始化进程才算结束:

 

a[i] = new Integer(rand.nextInt(500));

 

如果你忘记了创建对象,并且试图使用数组中的空引用,就会在运行时刻产生“异常”。

 

注意一下打印语句中String 对象的形成,Integer 对象的引用会自动转型,从而产生一个代表

对象内部值的字符

也可以用花括号括起来的列表来初始化对象数组。有两种形式:

 

//:c04:ArrayInit.java

// Arrayinitialization.

 

public   class ArrayInit {

public   static     void main(String[] args) {

    Integer[] a = {

new Integer(1),

new Integer(2),

new Integer(3),

    };

    Integer[] b = new Integer[] {

new Integer(1),

new Integer(2),

new Integer(3),

    };

  }

}   ///:~

 

第一种写法有时很有用,但由于数组的大小在编译期间就决定了,所以很受限制。初始化列

表的最后一个逗号是可选的(这一特性使维护长列表变得更容易)。

 

第二种形式提供了一种方便的语法来创建对象并调用方法,以获得与 C的“可变参数列表”

(C 通常把它简称为“varargs”)一致的效果。这可以应用于参数个数或类型未知的场合。

由于所有的类都直接或间接继承于 Object 类(随着本书的进展,你会对它有更深入的认识),

所以你可以创建以Object 数组为参数的方法,并这样调用:

 

//:c04:VarArgs.java

// Using arraysyntax to create variable argument lists.

import com.bruceeckel.simpletest.*;

class A { int i; }

public   class VarArgs {

static Test monitor = new Test();

static    void print(Object[] x) {

for(int i = 0; i < x.length; i++)

      System.out.println(x[i]);

  }

public   static     void main(String[] args) {

    print(new Object[] {

new Integer(47),    new VarArgs(),


new Float(3.14),    new Double(11.11)

    });

   print(new Object[] {"one",          "two",   "three" });

   print(new Object[] {new A(),         new A(),  new A()});

    monitor.expect(new Object[] {

"47",

"%%VarArgs@\\p{XDigit}+",

"3.14",

"11.11",

"one",

"two",

"three",

new TestExpression("%% A@\\p{XDigit}+", 3)

    });

  }

}   ///:~

 

你可以看到print( )方法使用 Object 数组作为参数,然后遍历数组,打印每个对象。标准 Java

库中的类能输出有意义的内容,但这里建立的类(A 和 VarArgs)的对象,打印出的内容只

是类的名称以及后面紧跟着的一个‘@’符号。代码里出现的正则表达式\p{XDigit},表示

一个十六进制数字。后面的‘+’表示会有一个或多个十六进制数字。于是,缺省行为(如

果你没有定义 toString( )方法的话,后面会讲这个方法的)就是打印类的名字和对象的地址。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值