按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
while(Math。random() != 0。0)
; // Keep trying
System。out。println(〃Produced 0。0!〃);
}
else if(args'0'。equals(〃upper〃)) {
while(Math。random() != 1。0)
; // Keep trying
System。out。println(〃Produced 1。0!〃);
}
else
usage();
}
} ///:~
为运行这个程序,只需在命令行键入下述命令即可:
java RandomBounds lower
或
java RandomBounds upper
在这两种情况下,我们都必须人工中断程序,所以会发现 Math。random() “似乎”永远都不会产生0。0 或
1。0。但这只是一项实验而已。若想到 0 和 1 之间有 2 的 128次方不同的双精度小数,所以如果全部产生这些
数字,花费的时间会远远超过一个人的生命。当然,最后的结果是在Math。random() 的输出中包括了0。0。或
者用数字语言表达,输出值范围是 '0;1)。
3。3 总结
本章总结了大多数程序设计语言都具有的基本特性:计算、运算符优先顺序、类型转换以及选择和循环等
等。现在,我们作好了相应的准备,可继续向面向对象的程序设计领域迈进。在下一章里,我们将讨论对象
的初始化与清除问题,再后面则讲述隐藏的基本实现方法。
3。4 练习
(1) 写一个程序,打印出 1 到 100 间的整数。
(2) 修改练习(1),在值为47 时用一个break 退出程序。亦可换成 return 试试。
(3) 创建一个 switch 语句,为每一种case 都显示一条消息。并将 switch置入一个 for 循环里,令其尝试每
一种case。在每个case 后面都放置一个break,并对其进行测试。然后,删除break,看看会有什么情况出
现。
94
…………………………………………………………Page 96……………………………………………………………
第 4 章 初始化和清除
“随着计算机的进步,‘不安全’的程序设计已成为造成编程代价高昂的罪魁祸首之一。”
“初始化”和“清除”是这些安全问题的其中两个。许多C 程序的错误都是由于程序员忘记初始化一个变量
造成的。对于现成的库,若用户不知道如何初始化库的一个组件,就往往会出现这一类的错误。清除是另一
个特殊的问题,因为用完一个元素后,由于不再关心,所以很容易把它忘记。这样一来,那个元素占用的资
源会一直保留下去,极易产生资源(主要是内存)用尽的后果。
C++为我们引入了“构建器”的概念。这是一种特殊的方法,在一个对象创建之后自动调用。Java 也沿用了
这个概念,但新增了自己的“垃圾收集器”,能在资源不再需要的时候自动释放它们。本章将讨论初始化和
清除的问题,以及 Java 如何提供它们的支持。
4。1 用构建器自动初始化
对于方法的创建,可将其想象成为自己写的每个类都调用一次 initialize()。这个名字提醒我们在使用对象
之前,应首先进行这样的调用。但不幸的是,这也意味着用户必须记住调用方法。在 Java 中,由于提供了名
为“构建器”的一种特殊方法,所以类的设计者可担保每个对象都会得到正确的初始化。若某个类有一个构
建器,那么在创建对象时,Java 会自动调用那个构建器——甚至在用户毫不知觉的情况下。所以说这是可以
担保的!
接着的一个问题是如何命名这个方法。存在两方面的问题。第一个是我们使用的任何名字都可能与打算为某
个类成员使用的名字冲突。第二是由于编译器的责任是调用构建器,所以它必须知道要调用是哪个方法。C++
采取的方案看来是最简单的,且更有逻辑性,所以也在Java 里得到了应用:构建器的名字与类名相同。这样
一来,可保证象这样的一个方法会在初始化期间自动调用。
下面是带有构建器的一个简单的类(若执行这个程序有问题,请参考第3 章的“赋值”小节)。
//: SimpleConstructor。java
// Demonstration of a simple constructor
package c04;
class Rock {
Rock() { // This is the constructor
System。out。println(〃Creating Rock〃);
}
}
public class SimpleConstructor {
public static void main(String'' args) {
for(int i = 0; i 《 10; i++)
new Rock();
}
} ///:~
现在,一旦创建一个对象:
new Rock();
就会分配相应的存储空间,并调用构建器。这样可保证在我们经手之前,对象得到正确的初始化。
请注意所有方法首字母小写的编码规则并不适用于构建器。这是由于构建器的名字必须与类名完全相同!
和其他任何方法一样,构建器也能使用自变量,以便我们指定对象的具体创建方式。可非常方便地改动上述
例子,以便构建器使用自己的自变量。如下所示:
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();
}
} ///:~
Tre