非也,错了,错了。下面看看think in java一书中对package的描述:
================================
进行面向对象的设计时,一项基本的考虑是:如何将发生变化的东西与
保持不变的东西分隔开。”
这一点对于库来说是特别重要的。那个库的用户(客户程序员)必须能依赖自己使用的那
一部分,并知道一旦新版本的库出台,自己不需要改写代码。而与此相反,库的创建者
必须能自由地进行修改与改进,同时保证客户程序员代码不会受到那些变动的影响。
为达到这个目的,需遵守一定的约定或规则。例如,库程序员在修改库内的一个类时,
必须保证不删除已有的方法,因为那样做会造成客户程序员代码出现断点。然而,
相反的情况却是令人痛苦的。对于一个数据成员,库的创建者怎样才能知道哪些数据成员
已受到客户程序员的访问呢?若方法属于某个类唯一的一部分,而且并不一定由客户程序
员直接使用,那么这种痛苦的情况同样是真实的。如果库的创建者想删除一种旧有的实施方
案,并置入新代码,此时又该怎么办呢?对那些成员进行的任何改动都可能中断客户程序员
的代码。所以库创建者处在一个尴尬的境地,似乎根本动弹不得。
为解决这个问题,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),而且它们由用于
主public类的“支撑”类组成。
编译一个.java文件时,我们会获得一个名字完全相同的输出文件;但对于.java文件中的
每个类,它们都有一个.class扩展名。因此,我们最终从少量的.java文件里有可能获得
数量众多的.class文件。如以前用一种汇编语言写过程序,那么可能已习惯编译器先
分割出一种过渡形式(通常是一个.obj文件),再用一个链接器将其与其他东西封装到一
起(生成一个可执行文件),或者与一个库封装到一起(生成一个库)。但那并不是Java
的工作方式。一个有效的程序就是一系列.class文件,它们可以封装和压缩到一个JAR文件
里(使用Java 1.1提供的jar工具)。Java解释器负责对这些文件的寻找、装载和解释
(注释①)。
①:Java并没有强制一定要使用解释器。一些固有代码的Java编译器可生成单独的可执行
文件。
“库”也由一系列类文件构成。每个文件都有一个public类(并没强迫使用一个public类,
但这种情况最很典型的),所以每个文件都有一个组件。如果想将所有这些组件
(它们在各自独立的.java和.class文件里)都归纳到一起,那么package关键字就可以发
挥作用)。
若在一个文件的开头使用下述代码:
package mypackage;
那么package语句必须作为文件的第一个非注释语句出现。该语句的作用是指出这个编译单
元属于名为mypackage的一个库的一部分。或者换句话说,它表明这个编译单元内的public
类名位于mypackage这个名字的下面。如果其他人想使用这个名字,要么指出完整的名字,
要么与mypackage联合使用import关键字(使用前面给出的选项)。注意根据Java包(封装)
的约定,名字内的所有字母都应小写,甚至那些中间单词亦要如此。
例如,假定文件名是MyClass.java。它意味着在那个文件有一个、而且只能有一个public类
。而且那个类的名字必须是MyClass(包括大小写形式):
package mypackage;
public class MyClass {
// . . .
现在,如果有人想使用MyClass,或者想使用mypackage内的其他任何public类,
他们必须用import关键字激活mypackage内的名字,使它们能够使用。
另一个办法则是指定完整的名称:mypackage.MyClass m = new mypackage.MyClass();
import关键字则可将其变得简洁得多:import mypackage.*;
// . . .
MyClass m = new MyClass();
作为一名库设计者,一定要记住package和import关键字允许我们做的事情就是分割单个
全局命名空间,保证我们不会遇到名字的冲突——无论有多少人使用因特网,
也无论多少人用Java编写自己的类。
================================================================================
怎么样?长是长了一些,但应该明白了吧,和压缩是没有关系的。
enjoy it!