按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
理过程中增加更大的安全性。有些垃圾收集器甚至能清除其他资源,比如图形和文件句柄等。然而,垃圾收
集器确实也增加了运行期的开销。但这种开销到底造成了多大的影响却是很难看出的,因为到目前为止,
Java 解释器的总体运行速度仍然是比较慢的。随着这一情况的改观,我们应该能判断出垃圾收集器的开销是
否使Java 不适合做一些特定的工作(其中一个问题是垃圾收集器不可预测的性质)。
由于所有对象都肯定能获得正确的构建,所以同这儿讲述的情况相比,构建器实际做的事情还要多得多。特
别地,当我们通过“创作”或“继承”生成新类的时候,对构建的保证仍然有效,而且需要一些附加的语法
来提供对它的支持。大家将在以后的章节里详细了解创作、继承以及它们对构建器造成的影响。
4。7 练习
(1) 用默认构建器创建一个类(没有自变量),用它打印一条消息。创建属于这个类的一个对象。
(2) 在练习 1 的基础上增加一个过载的构建器,令其采用一个String 自变量,并随同自己的消息打印出来。
(3) 以练习2 创建的类为基础上,创建属于它的对象句柄的一个数组,但不要实际创建对象并分配到数组
121
…………………………………………………………Page 123……………………………………………………………
里。运行程序时,注意是否打印出来自构建器调用的初始化消息。
(4) 创建同句柄数组联系起来的对象,最终完成练习3。
(5) 用自变量“before”,“after”和“none ”运行程序,试验Garbage。java。重复这个操作,观察是否
从输出中看出了一些固定的模式。改变代码,使System。runFinalization()在System。gc()之前调用,再观
察结果。
122
…………………………………………………………Page 124……………………………………………………………
第 5 章 隐藏实施过程
“进行面向对象的设计时,一项基本的考虑是:如何将发生变化的东西与保持不变的东西分隔开。”
这一点对于库来说是特别重要的。那个库的用户(客户程序员)必须能依赖自己使用的那一部分,并知道一
旦新版本的库出台,自己不需要改写代码。而与此相反,库的创建者必须能自由地进行修改与改进,同时保
证客户程序员代码不会受到那些变动的影响。
为达到这个目的,需遵守一定的约定或规则。例如,库程序员在修改库内的一个类时,必须保证不删除已有
的方法,因为那样做会造成客户程序员代码出现断点。然而,相反的情况却是令人痛苦的。对于一个数据成
员,库的创建者怎样才能知道哪些数据成员已受到客户程序员的访问呢?若方法属于某个类唯一的一部分,
而且并不一定由客户程序员直接使用,那么这种痛苦的情况同样是真实的。如果库的创建者想删除一种旧有
的实施方案,并置入新代码,此时又该怎么办呢?对那些成员进行的任何改动都可能中断客户程序员的代
码。所以库创建者处在一个尴尬的境地,似乎根本动弹不得。
为解决这个问题,Java 推出了“访问指示符”的概念,允许库创建者声明哪些东西是客户程序员可以使用
的,哪些是不可使用的。这种访问控制的级别在“最大访问”和“最小访问”的范围之间,分别包括:
public,“友好的”(无关键字),protected 以及private。根据前一段的描述,大家或许已总结出作为一
名库设计者,应将所有东西都尽可能保持为“private”(私有),并只展示出那些想让客户程序员使用的方
法。这种思路是完全正确的,尽管它有点儿违背那些用其他语言(特别是 C)编程的人的直觉,那些人习惯
于在没有任何限制的情况下访问所有东西。到这一章结束时,大家应该可以深刻体会到Java 访问控制的价
值。
然而,组件库以及控制谁能访问那个库的组件的概念现在仍不是完整的。仍存在这样一个问题:如何将组件
绑定到单独一个统一的库单元里。这是通过Java 的package (打包)关键字来实现的,而且访问指示符要受
到类在相同的包还是在不同的包里的影响。所以在本章的开头,大家首先要学习库组件如何置入包里。这样
才能理解访问指示符的完整含义。
5。1 包:库单元
我们用 import 关键字导入一个完整的库时,就会获得“包”(Package)。例如:
import java。util。*;
它的作用是导入完整的实用工具(Utility)库,该库属于标准Java 开发工具包的一部分。由于Vector 位于
java。util 里,所以现在要么指定完整名称“java。util。Vector”(可省略 import 语句),要么简单地指定
一个“Vector”(因为 import是默认的)。
若想导入单独一个类,可在 import语句里指定那个类的名字:
import java。util。Vector;
现在,我们可以自由地使用Vector。然而,java。util 中的其他任何类仍是不可使用的。
之所以要进行这样的导入,是为了提供一种特殊的机制,以便管理“命名空间”(Name Space)。我们所有
类成员的名字相互间都会隔离起来。位于类A 内的一个方法f()不会与位于类B 内的、拥有相同“签名”
(自变量列表)的f()发生冲突。但类名会不会冲突呢?假设创建一个stack 类,将它安装到已有一个 stack
类(由其他人编写)的机器上,这时会出现什么情况呢?对于因特网中的 Java 应用,这种情况会在用户毫不
知晓的时候发生,因为类会在运行一个Java 程序的时候自动下载。
正是由于存在名字潜在的冲突,所以特别有必要对 Java 中的命名空间进行完整的控制,而且需要创建一个完
全独一无二的名字,无论因特网存在什么样的限制。
迄今为止,本书的大多数例子都仅存在于单个文件中,而且设计成局部(本地)使用,没有同包名发生冲突
(在这种情况下,类名置于“默认包”内)。这是一种有效的做法,而且考虑到问题的简化,本书剩下的部
分也将尽可能地采用它。然而,若计划创建一个“对因特网友好”或者说“适合在因特网使用”的程序,必
须考虑如何防止类名的重复。
为Java 创建一个源码文件的时候,它通常叫作一个“编辑单元”(有时也叫作“翻译单元”)。每个编译单
元都必须有一个以。java 结尾的名字。而且在编译单元的内部,可以有一个公共(public)类,它必须拥有
与文件相同的名字(包括大小写形式,但排除。java文件扩展名)。如果不这样做,编译器就会报告出错。
每个编译单元内都只能有一个 public 类(同样地,否则编译器会报告出错)。那个编译单元剩下的类(如果
有的话)可在那个包外面的世界面前隐藏起来,因为它们并非“公共”的(非public),而且它们由用于主
123
…………………………………………………………Page 125……………………………………………………………
public 类的“支撑”类组成。
编译一个。java 文件时,我们会获得一个名字完全相同的输出文件;但对于。java 文件中的每个类,它们都有
一个。class 扩展名。因此,我们最终从少量的。java 文件里有可能获得数量众多的。class 文件。如以前用一
种汇编语言写过程序,那么可能已习惯编译器先分割出一种过渡形式(通常是一个。obj 文件),再用一个链
接器将其与其他东西封装到一起(生成一个可执行文件),或者与一个库封装到一起(生成一个库)。但那
并不是Java 的工作方式。一个有效的程序就是一系列。class 文件,它们可以封装和压缩到一个 JAR 文件里
(使用Java 1。1 提供的 jar 工具)。Java 解释器负责对这些文件的寻找、装载和解释(注释①