友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
第三电子书 返回本书目录 加入书签 我的书架 我的书签 TXT全本下载 『收藏到我的浏览器』

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

快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!


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

下面这个表达式:  

  

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 集合的“缺点”是在将对象置入一个集合时丢失了类型信息。之所以会发生这种情况,是由于当初 

编写集合时,那个集合的程序员根本不知道用户到底想把什么类型置入集合。若指示某个集合只允许特定的 

类型,会妨碍它成为一个“常规用途”的工具,为用户带来麻烦。为解决这个问题,集合实际容纳的是类型 

为Object 的一些对象的句柄。这种类型当然代表Java 中的所有对象,因为它是所有类的根。当然,也要注 

意这并不包括基本数据类型,因为它们并不是从“任何东西”继承来的。这是一个很好的方案,只是不适用 



                                                                              213 


…………………………………………………………Page 215……………………………………………………………

下述场合:  

(1) 将一个对象句柄置入集合时,由于类型信息会被抛弃,所以任何类型的对象都可进入我们的集合——即 

便特别指示它只能容纳特定类型的对象。举个例子来说,虽然指示它只能容纳猫,但事实上任何人都可以把 

一条狗扔进来。  

(2) 由于类型信息不复存在,所以集合能肯定的唯一事情就是自己容纳的是指向一个对象的句柄。正式使用 

它之前,必须对其进行造型,使其具有正确的类型。  

  

值得欣慰的是,Java 不允许人们滥用置入集合的对象。假如将一条狗扔进一个猫的集合,那么仍会将集合内 

的所有东西都看作猫,所以在使用那条狗时会得到一个“违例”错误。在同样的意义上,假若试图将一条狗 

的句柄“造型”到一只猫,那么运行期间仍会得到一个“违例”错误。  

下面是个例子:  

  

//: CatsAndDogs。java  

// Simple collection example (Vector)  

import java。util。*;  

  

class Cat {  

  private int catNumber;  

  Cat(int i) {  

    catNumber = i;  

  }  

  void print() {  

    System。out。println(〃Cat #〃 + catNumber);  

  }  

}  

  

class Dog {  

  private int dogNumber;  

  Dog(int i) {  

    dogNumber = i;  

  }  

  void print() {  

    System。out。println(〃Dog #〃 + dogNumber);  

  }  

}  

  

public class CatsAndDogs {  

  public static void main(String'' args) {  

    Vector cats = new Vector();  

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

      cats。addElement(new Cat(i));  

    // Not a problem to add a dog to cats:  

    cats。addElement(new Dog(7));  

    for(int i = 0; i 《 cats。size(); i++)  

      ((Cat)cats。elementAt(i))。print();  

    // Dog is detected only at run…time  

  }  

} ///:~  

  

可以看出,Vector 的使用是非常简单的:先创建一个,再用 addElement()置入对象,以后用 elementAt()取 

得那些对象(注意Vector 有一个 size()方法,可使我们知道已添加了多少个元素,以便防止误超边界,造 

成违例错误)。  



                                                                                             214 


…………………………………………………………Page 216……………………………………………………………

Cat 和 Dog 类都非常浅显——除了都是“对象”之外,它们并无特别之处(倘若不明确指出从什么类继承, 

就默认为从 Object 继承。所以我们不仅能用Vector 方法将 Cat 对象置入这个集合,也能添加 Dog 对象,同 

时不会在编译期和运行期得到任何出错提示。用Vector 方法 elementAt()获取原本认为是Cat 的对象时,实 

际获得的是指向一个Object 的句柄,必须将那个对象造型为Cat。随后,需要将整个表达式用括号封闭起 

来,在为Cat 调用print()方法之前进行强制造型;否则就会出现一个语法错误。在运行期间,如果试图将 

Dog 对象造型为 Cat,就会得到一个违例。  

这些处理的意义都非常深远。尽管显得有些麻烦,但却获得了安全上的保证。我们从此再难偶然造成一些隐 

藏得深的错误。若程序的一个部分(或几个部分)将对象插入一个集合,但我们只是通过一次违例在程序的 

某个部分发现一个错误的对象置入了集合,就必须找出插入错误的位置。当然,可通过检查代码达到这个目 

的,但这或许是最笨的调试工具。另一方面,我们可从一些标准化的集合类开始自己的编程。尽管它们在功 

能上存在一些不足,且显得有些笨拙,但却能保证没有隐藏的错误。  

  

1。 错误有时并不显露出来  

在某些情况下,程序似乎正确地工作,不造型回我们原来的类型。第一种情况是相当特殊的:String 类从编 

译器获得了额外的帮助,使其能够正常工作。只要编译器期待的是一个String 对象,但它没有得到一个,就 

会自动调用在Object 里定义、并且能够由任何Java 类覆盖的 toString()方法。这个方法能生成满足要求的 

String 对象,然后在我们需要的时候使用。  

因此,为了让自己类的对象能显示出来,要做的全部事情就是覆盖toString()方法,如下例所示:  

  

//: WorksAnyway。java  

// In special cases; things just seem  

// to work correctly。  

import java。util。*;  

  

class Mouse {  

  private int mouseNumber;  

  Mouse(int i) {  

    mouseNumber = i;  

  }  

  // Magic method:  

  public String toString() {  

    return 〃This is Mouse #〃 + mouseNumber;  

  }  

  void print(String msg) {  

    if(msg != null) System。out。println(msg);  

    System。out。println(  

      〃Mouse number 〃 + mouseNumber);  

  }  

}  

  

class MouseTrap {  

  static void caughtYa(Object m) {  

    Mouse mouse = (Mouse)m; // Cast from Object  

    mouse。print(〃Caught one!〃);  

  }  

}  

  

public class WorksAnyway {  

  public static void main(String'' args) {  

    Vector mice = new Vector();  

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

      mice。addElement(new Mouse(i));  



                                                                                           215 


…………………………………………………………Page 217……………………………………………………………

    for(int i = 0; i 
返回目录 上一页 下一页 回到顶部 1 1
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!