八宝书库 > 文学其他电子书 > Java编程思想第4版[中文版](PDF格式) >

第60部分

Java编程思想第4版[中文版](PDF格式)-第60部分

小说: Java编程思想第4版[中文版](PDF格式) 字数: 每页4000字

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!




下面这个例子展示了对数组进行初始化的不同方式,以及如何将数组句柄分配给不同的数组对象。它也揭示 



                                                             209 


…………………………………………………………Page 211……………………………………………………………

出对象数组和基本数据类型数组在使用方法上几乎是完全一致的。唯一的差别在于对象数组容纳的是句柄, 

而基本数据类型数组容纳的是具体的数值(若在执行此程序时遇到困难,请参考第3 章的“赋值”小节):  

  

//: ArraySize。java  

// Initialization & re…assignment of arrays  

package c08;  

  

class Weeble {} // A small mythical creature  

  

public class ArraySize {  

  public static void main(String'' args) {  

    // Arrays of objects:  

    Weeble'' a; // Null handle  

    Weeble'' b = new Weeble'5'; // Null handles  

    Weeble'' c = new Weeble'4';  

    for(int i = 0; i 《 c。length; i++)  

      c'i' = new Weeble();  

    Weeble'' d = {  

      new Weeble(); new Weeble(); new Weeble()  

    };  

    // pile error: variable a not initialized:  

    //!System。out。println(〃a。length=〃 + a。length);  

    System。out。println(〃b。length = 〃 + b。length);  

    // The handles inside the array are   

    // automatically initialized to null:  

    for(int i = 0; i 《 b。length; i++)  

      System。out。println(〃b'〃 + i + 〃'=〃 + b'i');  

    System。out。println(〃c。length = 〃 + c。length);  

    System。out。println(〃d。length = 〃 + d。length);  

    a = d;  

    System。out。println(〃a。length = 〃 + a。length);  

    // Java 1。1 initialization syntax:  

    a = new Weeble'' {  

      new Weeble(); new Weeble()  

    };  

    System。out。println(〃a。length = 〃 + a。length);  

  

    // Arrays of primitives:  

    int'' e; // Null handle  

    int'' f = new int'5';  

    int'' g = new int'4';  

    for(int i = 0; i 《 g。length; i++)  

      g'i' = i*i;  

    int'' h = { 11; 47; 93 };  

    // pile error: variable e not initialized:  

    //!System。out。println(〃e。length=〃 + e。length);  

    System。out。println(〃f。length = 〃 + f。length);  

    // The primitives inside the array are  

    // automatically initialized to zero:  

    for(int i = 0; i 《 f。length; i++)  

      System。out。println(〃f'〃 + i + 〃'=〃 + f'i');  

    System。out。println(〃g。length = 〃 + g。length);  



                                                                                          210 


…………………………………………………………Page 212……………………………………………………………

    System。out。println(〃h。length = 〃 + h。length);  

    e = h;  

    System。out。println(〃e。length = 〃 + e。length);  

    // Java 1。1 initialization syntax:  

    e = new int'' { 1; 2 };  

    System。out。println(〃e。length = 〃 + e。length);  

  }  

} ///:~  

Here’s the output from the program:  

  

  

b。length = 5  

b'0'=null  

b'1'=null  

b'2'=null  

b'3'=null  

b'4'=null  

c。length = 4  

d。length = 3  

a。length = 3  

a。length = 2  

f。length = 5  

f'0'=0  

f'1'=0  

f'2'=0  

f'3'=0  

f'4'=0  

g。length = 4  

h。length = 3  

e。length = 3  

e。length = 2  

  

其中,数组 a 只是初始化成一个 null 句柄。此时,编译器会禁止我们对这个句柄作任何实际操作,除非已正 

确地初始化了它。数组 b 被初始化成指向由 Weeble 句柄构成的一个数组,但那个数组里实际并未放置任何 

Weeble 对象。然而,我们仍然可以查询那个数组的大小,因为 b 指向的是一个合法对象。这也为我们带来了 

一个难题:不可知道那个数组里实际包含了多少个元素,因为 length 只告诉我们可将多少元素置入那个数 

组。换言之,我们只知道数组对象的大小或容量,不知其实际容纳了多少个元素。尽管如此,由于数组对象 

在创建之初会自动初始化成null,所以可检查它是否为 null,判断一个特定的数组“空位”是否容纳一个对 

象。类似地,由基本数据类型构成的数组会自动初始化成零(针对数值类型)、null (字符类型)或者 

false (布尔类型)。  

数组c 显示出我们首先创建一个数组对象,再将Weeble 对象赋给那个数组的所有“空位”。数组 d 揭示出 

 “集合初始化”语法,从而创建数组对象(用new 命令明确进行,类似于数组c),然后用Weeble 对象进行 

初始化,全部工作在一条语句里完成。  

下面这个表达式:  

  

a = d;  

  

向我们展示了如何取得同一个数组对象连接的句柄,然后将其赋给另一个数组对象,就象我们针对对象句柄 

的其他任何类型做的那样。现在,a 和 d 都指向内存堆内同样的数组对象。  

Java 1。1 加入了一种新的数组初始化语法,可将其想象成“动态集合初始化”。由 d 采用的 Java 1。0 集合 

初始化方法则必须在定义d 的同时进行。但若采用 Java 1。1 的语法,却可以在任何地方创建和初始化一个数 

组对象。例如,假设hide()方法用于取得一个Weeble 对象数组,那么调用它时传统的方法是:  



                                                                                          211 


…………………………………………………………Page 213……………………………………………………………

hide(d);  

但在Java 1。1 中,亦可动态创建想作为参数传递的数组,如下所示:  

hide(new Weeble'' {new Weeble(); new Weeble() });  

这一新式语法使我们在某些场合下写代码更方便了。  

上述例子的第二部分揭示出这样一个问题:对于由基本数据类型构成的数组,它们的运作方式与对象数组极 

为相似,只是前者直接包容了基本类型的数据值。  

  

1。 基本数据类型集合  

集合类只能容纳对象句柄。但对一个数组,却既可令其直接容纳基本类型的数据,亦可容纳指向对象的句 

柄。利用象 Integer、Double 之类的“封装器”类,可将基本数据类型的值置入一个集合里。但正如本章后 

面会在WordCount。java 例子中讲到的那样,用于基本数据类型的封装器类只是在某些场合下才能发挥作用。 

无论将基本类型的数据置入数组,还是将其封装进入位于集合的一个类内,都涉及到执行效率的问题。显 

然,若能创建和访问一个基本数据类型数组,那么比起访问一个封装数据的集合,前者的效率会高出许多。  

当然,假如准备一种基本数据类型,同时又想要集合的灵活性(在需要的时候可自动扩展,腾出更多的空 

间),就不宜使用数组,必须使用由封装的数据构成的一个集合。大家或许认为针对每种基本数据类型,都 

应有一种特殊类型的Vector。但Java 并未提供这一特性。某些形式的建模机制或许会在某一天帮助 Java 更 

好地解决这个问题(注释①)。  

  

①:这儿是 C++比Java 做得好的一个地方,因为C++通过 template 关键字提供了对“参数化类型”的支持。  



8。1。2  数组的返回  



假定我们现在想写一个方法,同时不希望它仅仅返回一样东西,而是想返回一系列东西。此时,象C 和C++ 

这样的语言会使问题复杂化,因为我们不能返回一个数组,只能返回指向数组的一个指针。这样就非常麻 

烦,因为很难控制数组的“存在时间”,它很容易造成内存“漏洞”的出现。  

Java 采用的是类似的方法,但我们能“返回一个数组”。当然,此时返回的实际仍是指向数组的指针。但在 

Java 里,我们永远不必担心那个数组的是否可用——只要需要,它就会自动存在。而且垃圾收集器会在我们 

完成后自动将其清除。  

作为一个例子,请思考如何返回一个字串数组:  

  

//: IceCream。java  

// Returning arrays from methods  

  

public class IceCream {  

  static String'' flav = {  

    〃Chocolate〃; 〃Strawberry〃;  

    〃Vanilla Fudge Swirl〃; 〃Mint Chip〃;  

    〃Mocha Almond Fudge〃; 〃Rum Raisin〃;  

    〃Praline Cream〃; 〃Mud Pie〃   

  };  

  static String'' flavorSet(int n) {  

    // Force it to be positive & within bounds:  

    n = Math。abs(n) % (flav。length + 1);  

    String'' results = new String'n';  

    int'' picks = new int'n';  

    for(int i = 0; i 《 picks。length; i++)  

      picks'i' = …1;  

    for(int i = 0; i 《 picks。length; i++) {  

      retry:  

      while(true) {  

        int t =  

           (int)(Math。random() * flav。length);  

        for(int j = 0; j 《 i; j++)  



                                                                                    212 


…………………………………………………………Page 214……………………………………………………………

          if(picks'j' == t) continue retry;  

        picks'i' = t;  

        results'i' = flav't';  

        break;  

      }  

    }  

    return results;  

  }  

  public static void main(String'' args) {  

    for(int i = 0; i 《 20; i++) {  

      System。out。println(  

        〃flavorSet(〃 + i + 〃) = 〃);  

      String'' fl = flavorSet(flav。length);  

      for(int j = 0; j 《 fl。length; j++)  

        System。out。println(〃t〃 + fl'j');  

    }  

  }  

} ///:~  

  

flavorSet()方法创建了一个名为 results 的String 数组。该数组的大小为 n——具体数值取决于我们传递 

给方法的自变量。随后,它从数组 flav 里随机挑选一些“香料”(Flavor),并将它们置入results 里,并 

最终返回results。返回数组与返回其他任何对象没什么区别——最终返回的都是一个句柄。至于数组到底 

是在flavorSet()里创建的,还是在其他什么地方创建的,这个问题并不重要,因为反正返回的仅是一个句 

柄。一旦我们的操作完成,垃圾收集器会自动关照数组的清除工作。而且只要我们需要数组,它就会乖乖地 

听候调遣。  

另一方面,注意当 flavorSet()随机挑选香料的时候,它需要保证以前出现过的一次随机选择不会再次出 

现。为达到这个目的,它使用了一个无限while 循环,不断地作出随机选择,直到发现未在picks 数组里出 

现过的一个元素为止(当然,也可以进行字串比较,检查随机选择是否在 results 数组里出现过,但字串比 

较的效率比较低)。若成功,就添加这个元素,并中断循环(break),再查找下一个(i 值会递增)。但假 

若 t 是一个已在 picks 里出现过的数组,就用标签式的continue 往回跳两级,强制选择一个新 t。用一个调 

试程序可以很清楚地看到这个过程。  

main()能显示出 20 个完整的香料集合,所以我们看到 flavorSet()每次都用一个随机顺序选择香料。为体会 

这一点,最简单的方法就是将输出重导向进入一个文件,然后直接观看这个文件的内容。  



8。2 集合  



现在总结一下我们前面学过的东西:为容纳一组对象,最适宜的选择应当是数组。而且假如容纳的是一系列 

基本数据类型,更是必须采用数组。在本章剩下的部分,大家将接触到一些更常规的情况。当我们编写程序 

时,通常并不能确切地知道最终需要多少个对象。有些时候甚至想用更复杂的方式来保存对象。为解决这个 

问题,Java 提供了四种类型的“集合类”:Vector (矢量)、BitSet (位集)、Stack (堆栈)以及 

Hashtable (散列表)。与拥有集合功能的其他语言相比,尽管这儿的数量显得相当少,但仍然能用它们解决 

数量惊人的实际问题。  

这些集合类具有形形色色的特征。例如,Stack 实现了一个 LIFO (先入先出)序列,而Hashtable 是一种 

 “关联数组”,允许我们将任何对象关联起来。除此以外,所有Java 集合类都能自动改变自身的大小。所 

以,我们在编程时可使用数量众多的对象,同时不必担心会将集合弄得有多大。  



8。2。1  缺点:类型未知  



使用Java 集合的“缺点”是在将对象置入一个集合时丢失了类型信息。之所以会发生这种情况,是由于当初 

返回目录 上一页 下一页 回到顶部 0 1

你可能喜欢的