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

第35部分

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

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

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




  

class Rock {  

  Rock(int i) {  



                                                                                  95 


…………………………………………………………Page 97……………………………………………………………

    System。out。println(  

      〃Creating Rock number 〃 + i);  

  }  

}  

  

public class SimpleConstructor {  

  public static void main(String'' args) {  

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

      new Rock(i);  

  }  

}  

  

利用构建器的自变量,我们可为一个对象的初始化设定相应的参数。举个例子来说,假设类 Tree 有一个构建 

器,它用一个整数自变量标记树的高度,那么就可以象下面这样创建一个 Tree 对象:  

  

tree t = new Tree(12); // 12 英尺高的树  

  

若Tree(int)是我们唯一的构建器,那么编译器不会允许我们以其他任何方式创建一个 Tree 对象。  

构建器有助于消除大量涉及类的问题,并使代码更易阅读。例如在前述的代码段中,我们并未看到对 

initialize()方法的明确调用——那些方法在概念上独立于定义内容。在Java 中,定义和初始化属于统一的 

概念——两者缺一不可。  

构建器属于一种较特殊的方法类型,因为它没有返回值。这与 void 返回值存在着明显的区别。对于void 返 

回值,尽管方法本身不会自动返回什么,但仍然可以让它返回另一些东西。构建器则不同,它不仅什么也不 

会自动返回,而且根本不能有任何选择。若存在一个返回值,而且假设我们可以自行选择返回内容,那么编 

译器多少要知道如何对那个返回值作什么样的处理。  



4。2 方法过载  



在任何程序设计语言中,一项重要的特性就是名字的运用。我们创建一个对象时,会分配到一个保存区域的 

名字。方法名代表的是一种具体的行动。通过用名字描述自己的系统,可使自己的程序更易人们理解和修 

改。它非常象写散文——目的是与读者沟通。  

我们用名字引用或描述所有对象与方法。若名字选得好,可使自己及其他人更易理解自己的代码。  

将人类语言中存在细致差别的概念“映射”到一种程序设计语言中时,会出现一些特殊的问题。在日常生活 

中,我们用相同的词表达多种不同的含义——即词的“过载”。我们说“洗衬衫”、“洗车”以及“洗 

狗”。但若强制象下面这样说,就显得很愚蠢:“衬衫洗 衬衫”、“车洗 车”以及“狗洗 狗”。这是由于 

听众根本不需要对执行的行动作任何明确的区分。人类的大多数语言都具有很强的“冗余”性,所以即使漏 

掉了几个词,仍然可以推断出含义。我们不需要独一无二的标识符——可从具体的语境中推论出含义。  

大多数程序设计语言(特别是 C)要求我们为每个函数都设定一个独一无二的标识符。所以绝对不能用一个 

名为print()的函数来显示整数,再用另一个print()显示浮点数——每个函数都要求具备唯一的名字。  

在Java 里,另一项因素强迫方法名出现过载情况:构建器。由于构建器的名字由类名决定,所以只能有一个 

构建器名称。但假若我们想用多种方式创建一个对象呢?例如,假设我们想创建一个类,令其用标准方式进 

行初始化,另外从文件里读取信息来初始化。此时,我们需要两个构建器,一个没有自变量(默认构建 

器),另一个将字串作为自变量——用于初始化对象的那个文件的名字。由于都是构建器,所以它们必须有 

相同的名字,亦即类名。所以为了让相同的方法名伴随不同的自变量类型使用,“方法过载”是非常关键的 

一项措施。同时,尽管方法过载是构建器必需的,但它亦可应用于其他任何方法,且用法非常方便。  

在下面这个例子里,我们向大家同时展示了过载构建器和过载的原始方法:  

  

//: Overloading。java  

// Demonstration of both constructor  

// and ordinary method overloading。  

import java。util。*;  

  

class Tree {  



                                                                               96 


…………………………………………………………Page 98……………………………………………………………

  int height;  

  Tree() {  

    prt(〃Planting a seedling〃);  

    height = 0;  

  }  

  Tree(int i) {  

    prt(〃Creating new Tree that is 〃  

        + i + 〃 feet tall〃);  

    height = i;  

  }  

  void info() {  

    prt(〃Tree is 〃 + height  

        + 〃 feet tall〃);  

  }  

  void info(String s) {  

    prt(s + 〃: Tree is 〃  

        + height + 〃 feet tall〃);  

  }  

  static void prt(String s) {  

    System。out。println(s);  

  }  

}  

  

public class Overloading {  

  public static void main(String'' args) {  

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

      Tree t = new Tree(i);  

      t。info();  

      t。info(〃overloaded method〃);  

    }  

    // Overloaded constructor:  

    new Tree();  

  }  

} ///:~  

  

Tree 既可创建成一颗种子,不含任何自变量;亦可创建成生长在苗圃中的植物。为支持这种创建,共使用了 

两个构建器,一个没有自变量(我们把没有自变量的构建器称作“默认构建器”,注释①),另一个采用现 

成的高度。  

  

①:在 Sun 公司出版的一些Java 资料中,用简陋但很说明问题的词语称呼这类构建器—— “无参数构建器” 

 (no…arg constructors)。但“默认构建器”这个称呼已使用了许多年,所以我选择了它。  

  

我们也有可能希望通过多种途径调用 info()方法。例如,假设我们有一条额外的消息想显示出来,就使用 

String 自变量;而假设没有其他话可说,就不使用。由于为显然相同的概念赋予了两个独立的名字,所以看 

起来可能有些古怪。幸运的是,方法过载允许我们为两者使用相同的名字。  



4。2。1  区分过载方法  



若方法有同样的名字,Java 怎样知道我们指的哪一个方法呢?这里有一个简单的规则:每个过载的方法都必 

须采取独一无二的自变量类型列表。  

若稍微思考几秒钟,就会想到这样一个问题:除根据自变量的类型,程序员如何区分两个同名方法的差异 

呢?  

即使自变量的顺序也足够我们区分两个方法(尽管我们通常不愿意采用这种方法,因为它会产生难以维护的 



                                                                                              97 


…………………………………………………………Page 99……………………………………………………………

代码):  

  

//: OverloadingOrder。java  

// Overloading based on the order of  

// the arguments。  

  

public class OverloadingOrder {  

  static void print(String s; int i) {  

    System。out。println(  

      〃String: 〃 + s +  

      〃; int: 〃 + i);  

  }  

  static void print(int i; String s) {  

    System。out。println(  

      〃int: 〃 + i +  

      〃; String: 〃 + s);  

  }  

  public static void main(String'' args) {  

    print(〃String first〃; 11);  

    print(99; 〃Int first〃);  

  }  

} ///:~  

  

两个print()方法有完全一致的自变量,但顺序不同,可据此区分它们。  



4。2。2  主类型的过载  



主(数据)类型能从一个“较小”的类型自动转变成一个“较大”的类型。涉及过载问题时,这会稍微造成 

一些混乱。下面这个例子揭示了将主类型传递给过载的方法时发生的情况:  

  

//: PrimitiveOverloading。java  

// Promotion of primitives and overloading  

  

public class PrimitiveOverloading {  

  // boolean can't be automatically converted  

  static void prt(String s) {   

    System。out。println(s);   

  }  

  

  void f1(char x) { prt(〃f1(char)〃); }  

  void f1(byte x) { prt(〃f1(byte)〃); }  

  void f1(short x) { prt(〃f1(short)〃); }  

  void f1(int x) { prt(〃f1(int)〃); }  

  void f1(long x) { prt(〃f1(long)〃); }  

  void f1(float x) { prt(〃f1(float)〃); }  

  void f1(double x) { prt(〃f1(double)〃); }  

  

  void f2(byte x) { prt(〃f2(byte)〃); }  

  void f2(short x) { prt(〃f2(short)〃); }  

  void f2(int x) { prt(〃f2(int)〃); }  

  void f2(long x) { prt(〃f2(long)〃); }  

  void f2(float x) { prt(〃f2(float)〃); }  

  void f2(double x) { prt(〃f2(double)〃); }  



                                                                                           98 


…………………………………………………………Page 100……………………………………………………………

  

  void f3(short x) { prt(〃f3(short)〃); }  

  void f3(int x) { prt(〃f3(int)〃); }  

  void f3(long x) { prt(〃f3(long)〃); }  

  void f3(float x) { prt(〃f3(float)〃); }  

  void f3(double x) { prt(〃f3(double)〃); }  

  

  void f4(int x) { prt(〃f4(int)〃); }  

  void f4(long x) { prt(〃f4(long)〃); }  

  void f4(float x) { prt(〃f4(float)〃); }  

  void f4(double x) { prt(〃f4(double)〃); }  

  

  void f5(long x) { prt(〃f5(long)〃); }  

  void f5(float x) { prt(〃f5(float)〃); }  

  void f5(double x) { prt(〃f5(double)〃); }  

  

  void f6(float x) { prt(〃f6(float)〃); }  

  void f6(double x) { prt(〃f6(double)〃); }  

  

  void f7(double x) { prt(〃f7(double)〃); }  

  

  void testConstVal() {  

    prt(〃Testing with 5〃);  

    f1 (5);f2(5);f3(5);f4(5);f5(5);f6(5);f7(5);  

  }  

  void testChar() {  

    char x = 'x';  

    prt(〃char argument:〃);  

    f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);  

  }  

  void testByte() {  

    byte x = 0;  

    prt(〃byte argument:〃);  

    f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);  

  }  

  void testShort() {  

    short x = 0;  

    prt(〃short argument:〃);  

    f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);  

  }  

  void testInt() {  

    int x = 0;  

    prt(〃int argument:〃);  

    f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);  

  }  

  void testLong() {  

    long x = 0;  

    prt(〃long argument:〃);  

    f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);  

  }  

  void testFloat() {  

    float x = 0;  



                                                                                           99 


…………………………………………………………Page 101……………………………………………………………

    prt(〃float argument:〃);  

    f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);  

  }  

  void testDouble() {  

    double x = 0;  

    prt(〃double argument:〃);  

    f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);  

  }  

  public static void main(String'' args) {  

    PrimitiveOverloading p =   

      new PrimitiveOverloading();  

    p。testConstVal();  

    p。testChar();  

    p。testByte();  

    p。testShort();  

    p。testInt();  

    p。testLong();  

    p。testFloat();  

    p。testDouble();  

  }  

} ///:~  

  

若观察这个程序的输出,就会发现常数值 5 被当作一个 int值处理。所以假若可以使用一个过载的方法,就 

能获取它使用的int值。在其他所有情况下,若我们的数据类型“小于”方法中使用的自变量,就会对那种 

数据类型进行“转型”处理。char 获得的效果稍有些不同,这是由于假期它没有发现一个准确的 char 匹 

配,就会转型为 int。  

若我们的自变量“大于”过载方法期望的自变量,这时又会出现什么情况呢?对前述程序的一个修改揭示出 

了答案:  

  

//: Demotion。java  

// Demotion of primitives and overloading  

  

public class Demotion {  

  static void prt(String s) {   

    System。out。println(s);   

  }  

  

  void f1(char x) { prt(〃f1(char)〃); }  

  void f1(byte x) { prt(〃f1(byte

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

你可能喜欢的