- 写在前面的话
- 引言
- 第 1 章 对象入门
- 第 2 章 一切都是对象
- 第 3 章 控制程序流程
- 第 4 章 初始化和清除
- 第 5 章 隐藏实施过程
- 第 6 章 类再生
- 第 7 章 多形性
- 第 8 章 对象的容纳
- 第 9 章 违例差错控制
- 第 10 章 Java IO 系统
- 第 11 章 运行期类型鉴定
- 第 12 章 传递和返回对象
- 第 十三 章 创建窗口和程序片
- 第 14 章 多线程
- 第 15 章 网络编程
- 第 16 章 设计范式
- 第 17 章 项目
- 附录 A 使用非 JAVA 代码
- 附录 B 对比 C++和 Java
- 附录 C Java 编程规则
- 附录 D 性能
- 附录 E 关于垃圾收集的一些话
- 附录 F 推荐读物
5.4 类访问
在 Java 中,亦可用访问指示符判断出一个库内的哪些类可由那个库的用户使用。若想一个类能由客户程序员调用,可在类主体的起始花括号前面某处放置一个 public 关键字。它控制着客户程序员是否能够创建属于这个类的一个对象。
为控制一个类的访问,指示符必须在关键字 class 之前出现。所以我们能够使用:
public class Widget {
也就是说,假若我们的库名是 mylib,那么所有客户程序员都能访问 Widget——通过下述语句:
import mylib.Widget;
或者
import mylib.*;
然而,我们同时还要注意到一些额外的限制:
(1) 每个编译单元(文件)都只能有一个 public 类。每个编译单元有一个公共接口的概念是由那个公共类表达出来的。根据自己的需要,它可拥有任意多个提供支撑的“友好”类。但若在一个编译单元里使用了多个 public 类,编译器就会向我们提示一条出错消息。
(2) public 类的名字必须与包含了编译单元的那个文件的名字完全相符,甚至包括它的大小写形式。所以对于 Widget 来说,文件的名字必须是 Widget.java,而不应是 widget.java 或者 WIDGET.java。同样地,如果出现不符,就会报告一个编译期错误。
(3) 可能(但并常见)有一个编译单元根本没有任何公共类。此时,可按自己的意愿任意指定文件名。
如果已经获得了 mylib 内部的一个类,准备用它完成由 Widget 或者 mylib 内部的其他某些 public 类执行的任务,此时又会出现什么情况呢?我们不希望花费力气为客户程序员编制文档,并感觉以后某个时候也许会进行大手笔的修改,并将自己的类一起删掉,换成另一个不同的类。为获得这种灵活处理的能力,需要保证没有客户程序员能够依赖自己隐藏于 mylib 内部的特定实施细节。为达到这个目的,只需将 public 关键字从类中剔除即可,这样便把类变成了“友好的”(类仅能在包内使用)。
注意不可将类设成 private(那样会使除类之外的其他东西都不能访问它),也不能设成 protected(注释④)。因此,我们现在对于类的访问只有两个选择:“友好的”或者 public。若不愿其他任何人访问那个类,可将所有构建器设为 private。这样一来,在类的一个 static 成员内部,除自己之外的其他所有人都无法创建属于那个类的一个对象(注释⑤)。如下例所示:
//: Lunch.java // Demonstrates class access specifiers. // Make a class effectively private // with private constructors: class Soup { private Soup() {} // (1) Allow creation via static method: public static Soup makeSoup() { return new Soup(); } // (2) Create a static object and // return a reference upon request. // (The "Singleton" pattern): private static Soup ps1 = new Soup(); public static Soup access() { return ps1; } public void f() {} } class Sandwich { // Uses Lunch void f() { new Lunch(); } } // Only one public class allowed per file: public class Lunch { void test() { // Can't do this! Private constructor: //! Soup priv1 = new Soup(); Soup priv2 = Soup.makeSoup(); Sandwich f1 = new Sandwich(); Soup.access().f(); } } ///:~
④:实际上,Java 1.1 内部类既可以是“受到保护的”,也可以是“私有的”,但那属于特别情况。第 7 章会详细解释这个问题。
⑤:亦可通过从那个类继承来实现。
迄今为止,我们创建过的大多数方法都是要么返回 void,要么返回一个基本数据类型。所以对下述定义来说:
public static Soup access() {
return psl;
}
它最开始多少会使人有些迷惑。位于方法名(access)前的单词指出方法到底返回什么。在这之前,我们看到的都是 void,它意味着“什么也不返回”(void 在英语里是“虚无”的意思。但亦可返回指向一个对象的句柄,此时出现的就是这个情况。该方法返回一个句柄,它指向类 Soup 的一个对象。
Soup 类向我们展示出如何通过将所有构建器都设为 private,从而防止直接创建一个类。请记住,假若不明确地至少创建一个构建器,就会自动创建默认构建器(没有自变量)。若自己编写默认构建器,它就不会自动创建。把它变成 private 后,就没人能为那个类创建一个对象。但别人怎样使用这个类呢?上面的例子为我们揭示出了两个选择。第一个选择,我们可创建一个 static 方法,再通过它创建一个新的 Soup,然后返回指向它的一个句柄。如果想在返回之前对 Soup 进行一些额外的操作,或者想了解准备创建多少个 Soup 对象(可能是为了限制它们的个数),这种方案无疑是特别有用的。
第二个选择是采用“设计方案”(Design Pattern)技术,本书后面会对此进行详细介绍。通常方案叫作“独子”,因为它仅允许创建一个对象。类 Soup 的对象被创建成 Soup 的一个 static private 成员,所以有一个而且只能有一个。除非通过 public 方法 access(),否则根本无法访问它。
正如早先指出的那样,如果不针对类的访问设置一个访问指示符,那么它会自动默认为“友好的”。这意味着那个类的对象可由包内的其他类创建,但不能由包外创建。请记住,对于相同目录内的所有文件,如果没有明确地进行 package 声明,那么它们都默认为那个目录的默认包的一部分。然而,假若那个类一个 static 成员的属性是 public,那么客户程序员仍然能够访问那个 static 成员——即使它们不能创建属于那个类的一个对象。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论