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

第39部分

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

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

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




行。因此,static初始化仅发生一次——在 Class 对象首次载入的时候。  

(3) 创建一个new Dog()时,Dog 对象的构建进程首先会在内存堆(Heap )里为一个Dog 对象分配足够多的存 

储空间。  

(4) 这种存储空间会清为零,将Dog 中的所有基本类型设为它们的默认值(零用于数字,以及boolean 和 

char 的等价设定)。  

(5) 进行字段定义时发生的所有初始化都会执行。  

(6) 执行构建器。正如第6 章将要讲到的那样,这实际可能要求进行相当多的操作,特别是在涉及继承的时 

候。  

  

3。 明确进行的静态初始化  

Java 允许我们将其他static初始化工作划分到类内一个特殊的“static 构建从句”(有时也叫作“静态 

块”)里。它看起来象下面这个样子:  

  

class Spoon {  

  static int i;  

  static {  

    i = 47;  

  }  

  // 。 。 。  

  

尽管看起来象个方法,但它实际只是一个 static 关键字,后面跟随一个方法主体。与其他 static初始化一 

样,这段代码仅执行一次——首次生成那个类的一个对象时,或者首次访问属于那个类的一个 static 成员时 

 (即便从未生成过那个类的对象)。例如:  

  

//: ExplicitStatic。java  

// Explicit static initialization  

// with the 〃static〃 clause。  

  

class Cup {  

  Cup(int marker) {  

    System。out。println(〃Cup(〃 + marker + 〃)〃);  

  }  

  void f(int marker) {  

    System。out。println(〃f(〃 + marker + 〃)〃);  

  }  

}  

  

class Cups {  

  static Cup c1;  

  static Cup c2;  

  static {  

    c1 = new Cup(1);  

    c2 = new Cup(2);  

  }  

  Cups() {  

    System。out。println(〃Cups()〃);  



                                                                                         114 


…………………………………………………………Page 116……………………………………………………………

  }  

}  

  

public class ExplicitStatic {  

  public static void main(String'' args) {  

    System。out。println(〃Inside main()〃);  

    Cups。c1。f(99);  // (1)  

  }  

  static Cups x = new Cups();  // (2)  

  static Cups y = new Cups();  // (2)   

} ///:~  

  

在标记为(1)的行内访问 static 对象 c1 的时候,或在行(1)标记为注释,同时 (2)行不标记成注释的时候,用 

于Cups 的 static初始化模块就会运行。若(1)和 (2)都被标记成注释,则用于 Cups 的static 初始化进程永 

远不会发生。  

  

4。 非静态实例的初始化  

针对每个对象的非静态变量的初始化,Java 1。1 提供了一种类似的语法格式。下面是一个例子:  

  

//: Mugs。java  

// Java 1。1 〃Instance Initialization〃  

  

class Mug {  

  Mug(int marker) {  

    System。out。println(〃Mug(〃 + marker + 〃)〃);  

  }  

  void f(int marker) {  

    System。out。println(〃f(〃 + marker + 〃)〃);  

  }  

}  

  

public class Mugs {  

  Mug c1;  

  Mug c2;  

  {  

    c1 = new Mug(1);  

    c2 = new Mug(2);  

    System。out。println(〃c1 & c2 initialized〃);  

  }  

  Mugs() {  

    System。out。println(〃Mugs()〃);  

  }  

  public static void main(String'' args) {  

    System。out。println(〃Inside main()〃);  

    Mugs x = new Mugs();  

  }  

} ///:~  

  

大家可看到实例初始化从句:  

  

  {  

    c1 = new Mug(1);  



                                                                                             115 


…………………………………………………………Page 117……………………………………………………………

    c2 = new Mug(2);  

    System。out。println(〃c1 & c2 initialized〃);  

  }  

  

它看起来与静态初始化从句极其相似,只是 static 关键字从里面消失了。为支持对“匿名内部类”的初始化 

 (参见第7 章),必须采用这一语法格式。  



4。5 数组初始化  



在C 中初始化数组极易出错,而且相当麻烦。C++通过“集合初始化”使其更安全(注释⑥)。Java 则没有 

象C++那样的“集合”概念,因为Java 中的所有东西都是对象。但它确实有自己的数组,通过数组初始化来 

提供支持。  

数组代表一系列对象或者基本数据类型,所有相同的类型都封装到一起——采用一个统一的标识符名称。数 

组的定义和使用是通过方括号索引运算符进行的( '')。为定义一个数组,只需在类型名后简单地跟随一对 

空方括号即可:  

int'' al;  

也可以将方括号置于标识符后面,获得完全一致的结果:  

int al'';  

这种格式与 C 和 C++程序员习惯的格式是一致的。然而,最“通顺”的也许还是前一种语法,因为它指出类 

型是“一个 int 数组”。本书将沿用那种格式。  

编译器不允许我们告诉它一个数组有多大。这样便使我们回到了“句柄”的问题上。此时,我们拥有的一切 

就是指向数组的一个句柄,而且尚未给数组分配任何空间。为了给数组创建相应的存储空间,必须编写一个 

初始化表达式。对于数组,初始化工作可在代码的任何地方出现,但也可以使用一种特殊的初始化表达式, 

它必须在数组创建的地方出现。这种特殊的初始化是一系列由花括号封闭起来的值。存储空间的分配(等价 

于使用new)将由编译器在这种情况下进行。例如:  

int'' a1 = { 1; 2; 3; 4; 5 };  

那么为什么还要定义一个没有数组的数组句柄呢?  

int'' a2;  

事实上在Java 中,可将一个数组分配给另一个,所以能使用下述语句:  

a2 = a1;  

我们真正准备做的是复制一个句柄,就象下面演示的那样:  

  

//: Arrays。java  

// Arrays of primitives。  

  

public class Arrays {  

  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++)  

      prt(〃a1'〃 + i + 〃' = 〃 + a1'i');  

  }  

  static void prt(String s) {  

    System。out。println(s);  

  }  

} ///:~  

  

大家看到a1 获得了一个初始值,而 a2 没有;a2 将在以后赋值——这种情况下是赋给另一个数组。  

这里也出现了一些新东西:所有数组都有一个本质成员(无论它们是对象数组还是基本类型数组),可对其 

进行查询——但不是改变,从而获知数组内包含了多少个元素。这个成员就是 length。与C 和 C++类似,由 



                                                                                    116 


…………………………………………………………Page 118……………………………………………………………

于Java 数组从元素 0 开始计数,所以能索引的最大元素编号是“length…1”。如超出边界,C 和C++会“默 

默”地接受,并允许我们胡乱使用自己的内存,这正是许多程序错误的根源。然而,Java 可保留我们这受这 

一问题的损害,方法是一旦超过边界,就生成一个运行期错误(即一个“违例”,这是第9 章的主题)。当 

然,由于需要检查每个数组的访问,所以会消耗一定的时间和多余的代码量,而且没有办法把它关闭。这意 

味着数组访问可能成为程序效率低下的重要原因——如果它们在关键的场合进行。但考虑到因特网访问的安 

全,以及程序员的编程效率,Java 设计人员还是应该把它看作是值得的。  

程序编写期间,如果不知道在自己的数组里需要多少元素,那么又该怎么办呢?此时,只需简单地用new 在 

数组里创建元素。在这里,即使准备创建的是一个基本数据类型的数组,new 也能正常地工作(new 不会创建 

非数组的基本类型):  

  

//: ArrayNew。java  

// Creating arrays with new。  

import java。util。*;  

  

public class ArrayNew {  

  static Random rand = new Random();  

  static int pRand(int mod) {  

    return Math。abs(rand。nextInt()) % mod + 1;  

  }  

  public static void main(String'' args) {  

    int'' a;  

    a = new int'pRand(20)';  

    prt(〃length of a = 〃 + a。length);  

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

      prt(〃a'〃 + i + 〃' = 〃 + a'i');  

  }  

  static void prt(String s) {  

    System。out。println(s);  

  }  

} ///:~  

  

由于数组的大小是随机决定的(使用早先定义的pRand()方法),所以非常明显,数组的创建实际是在运行 

期间进行的。除此以外,从这个程序的输出中,大家可看到基本数据类型的数组元素会自动初始化成“空” 

值(对于数值,空值就是零;对于 char,它是null ;而对于boolean,它却是false)。  

当然,数组可能已在相同的语句中定义和初始化了,如下所示:  

int'' a = new int'pRand(20)';  

若操作的是一个非基本类型对象的数组,那么无论如何都要使用new。在这里,我们会再一次遇到句柄问 

题,因为我们创建的是一个句柄数组。请大家观察封装器类型 Integer,它是一个类,而非基本数据类型:  

  

//: ArrayClassObj。java  

// Creating an array of non…primitive objects。  

import java。util。*;  

  

public class ArrayClassObj {  

  static Random rand = new Random();  

  static int pRand(int mod) {  

    return Math。abs (rand。nextInt()) % mod + 1;  

  }  

  public static void main(String'' args) {  

    Integer'' a = new Integer'pRand(20)';  

    prt(〃length of a = 〃 + a。length);  

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



                                                                                          117 


…………………………………………………………Page 119……………………………………………………………

      a'i' = new Integer(pRand(500));  

      prt(〃a'〃 + i + 〃' = 〃 + a'i');  

    }  

  }  

  static void prt(String s) {  

    System。out。println(s);  

  }  

} ///:~  

  

在这儿,甚至在new 调用后才开始创建数组:  

Integer'' a = new Integer'pRand(20)';  

它只是一个句柄数组,而且除非通过创建一个新的 Integer对象,从而初始化了对象句柄,否则初始化进程 

不会结束:  

a'i' = new Integer(pRand(500));  

但若忘记创建对象,就会在运行期试图读取空数组位置时获得一个“违例”错误。  

下面让我们看看打印语句中String 对象的构成情况。大家可看到指向 Integer 对象的句柄会自动转换,从而 

产生一个String,它代表着位于对象内部的值。  

亦可用花括号封闭列表来初始化对象数组。可采用两种形式,第一种是Java 1。0 允许的唯一形式。第二种 

 (等价)形式自Java 1。1 才开始提供支持:  

  

//: ArrayInit。java  

// Array initialization  

  

public class ArrayInit {  

  public static void main(String'' args) {  

    Integer'' a = {  

      new Integer(1);  

      new Integer(2);  

      new Integer(3);  

    };  

  

    // Java 1。1 only:  

    Integer'' b = new Integer'' {  

      new Integer(1);  

      new Integer(2);  

      new Integer(3);  

    };  

  }  

} ///:~  

  

这种做法大多数时候都很有用,但限制也是最大的,因为数组的大小是在编译期间决定的。初始化列表的最 

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

数组初始化的第二种形式(Java 1。1 开始支持)提供了一种更简便的语法,可创建和调用方法,获得与C 的 

 “变量参数列表”(C 通常把它简称为“变参表”)一致的效果。这些效果包括未知的参数(自变量)数量 

以及未知的类型(如果这样选择的话)。由于所有类最终都是从通用的根类Object 中继承的,所以能创建一 

个方法,令其获取一个 Object 数组,并象下面这样调用它:  

  

//: VarArgs。java  

// Using the Java 1。1 array syntax to create  

// variable argument lists  

  

class A { int i; }  



                                                     

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

你可能喜欢的