详解Java编程中包package的内容与包对象的规范
包的内容
包的内容应该仔细设计,使其只包含在功能上相关的类和接口。包中的类可以自由地访问该包中其他类的非私有成员,有些类甚至可能有足够的权限去访问其他类的内部细节,为了避免这样的类对类成员进行误操作,我们需要对类成员进行保护。任何没有被声明为private的成员都可以被同一个包中的其他所有类型访问,所以任何不相关的类之间的藕合程度都可能会比我们所期望的程度高。
包还为寻找有用的接口和类的程序员提供了逻辑分组的功能。由不相关的类组成的包使程序员很难分辨出哪些接口和类是有用的,而类的逻辑分组可以帮助程序员重用代码,因为程序员通过逻辑分组能够更容易地找到他们所需要的东西。如果包中只包含相关的、紧藕合的类型集,则意味着我们可以给类型取一些更直观的名字,从而避免名字冲突。
包可以嵌套。例如,java.lang就是一个嵌套包,其中,包Lang嵌套在更大的包java中,而包java却还包含一些其他的包。嵌套使得相关的包构成了具有层次结构的命名系统。
例如,为了创建一组包,用于诸如神经网络和遗传算法这样的自适应系统,我们可以用以圆点分隔的名字来命名包,从而创建嵌套包:
packageadaptive.neuralNet;
含有上面这条声明语句的源文件位于adaptive.neuralNet包中,而adaptive.neuralNet包本身又是adaptive包的子包。adaptive包中可能包含一些与通用的自适应算法相关的类,例如泛化问题陈述类或基准测试类。在层次结构中处于更深位置的包(例如adaptive.neu-ralNet或adaptive.genetic)包含与特定类型的自适应算法相关的类。
包的嵌套仅仅是组织相关包的一种工具,它并不能提供包之间的任何特殊的访问权限。
adaptive.genetic包中的类代码无法访问adaptive或adaptive.neuralNet包中具有包访问权限的成员,包作用域只适用于特定的包。包的嵌套可以对相关的包进行分组,并帮助程序员更方便地在逻辑层次中找到想要的类,但是除此之外,它并未带来其他的任何益处。
包的注解
包也可以有注解。但是问题在于,由于包是一种组织结构,没有源代码实体,它们并没有实际的定义,所以不能像对类或方法那样对它们进行注解,因此包的注解只能通过在源文件中对包的声明语句进行注解来实现。然而,在每个包中只能有一个包声明可以拥有作用于它的注解。
那么究竟如何对包进行注解呢?事实上,Java语言并没有强制程序员必须使用某种方式来处理“单一注解的包语句”规则。所建议的方式是在包目录中创建一个名为package一info.java的文件,在这个文件中只存储包语句和该包的注解,而不放置任何其他内容。例如,用于attr包的package一info.java文件看起来就是这样的:
@PackageSpec(name二”AttrProject",version="1.0" @DevelopmentSite("attr.project.org") @DevelopmentModel("open一source") packageattr;
其中Packagespec,Developmentsite和Developmentmodel用来修饰注解类型,当然,它们具有运行时的保存策略。package一info.java文件应该和包中的其他源文件一起编译。
我们推荐将所有与包相关的信息都放置在package一info.java文件中。如果你这样做了,那么你就可以在文件的开头放置文档注释,从而使这些文档被注释成包文档。
包对象和规范
包通常会实现某种规范,并且通常是来自于某个组织的。Package对象与其他的反射类型不同,不能用来创建或操作包,而只能充当提供信息的知识库,用来提供有关包所实现的规范的信息(规范的标题、供应商和版本号)和有关包的实现本身的信息(包的标题、供应商和版本号)。虽然包通常来自于单个的组织,但它所实现的规范(如统计分析库)却可能是其他组织已定义过的。使用包的程序可能需要知道该包所实现的规范的版本,从而可以使用只在某个版本中定义的功能。类似地,这些程序还可能需要知道提供给它的是哪个实现版本,这主要是为了处理在不同版本中可能存在的缺陷。Package类的一些主要方法允许访问到这些信息:
- ·publicStringgetName():返回该包的名字。
- .publicstringgetspecificationTitlep:返回该包所实现的规范的标题,如果标题未知,则返回null,
- .publicstringgetspecificationversion():返回一个描述该包所实现的规范的版本信息的字符串,如果版本信息未知,则返回null,
- .publicstringgetspecificationvendorQ:返回供应商的名字,这个供应商拥有并维护该包所实现的规范,如果供应商未知,则返回null,
- .publicstringgetImplerentationTitle():返回该包所提供的实现的标题,如果标题未知,则返回null,·publicstringgetImplementationversion():返回一个描述该包所提供的实现的版本信息的字符串,如果版本信息未知,则返回null,
- ·publicstringgetImplementationvendor():返回提供该实现的组织(供应商)的名字,如果该组织未知,则返回null,
例如,在我们的系统中提取java.lang包的这些信息,将会得到如下结果:'
SpecificationTitle:JavaPlatformAPISpecification SpecificationVersion:1.4 SpecificationVendor:SunMicrosystems,Inc. ImplementationTitle:JavaRuntimeEnvironment ImplementationVersion:1.5.0_02 ImplementationVendor:SunMicrosystems,Inc.
规范版本号由句点分隔符分开的非负数字组成,如‘'2.0'‘或”11.0.12"。这种模式使得我们可以调用iscompatiblewith方法对遵循该模式的版本号与包的版本号进行比较。如果包的版本号大于等于传人的版本号,那么该方法就返回true。这种比较每次只比较一个由句点分隔的数字,如果这些数字中任何一个小于传递进来的版本号中对应位置的值,那么这两个版本就不兼容。如果其中一个版本号比另一个长,那么在短的版本号中缺少的部分将被认为是零。例如,如果包的规范版本号是”1.4",并且我们用iscompatiblewith方法将其与”1.2","1.3.1'.或”.1.81.进行比较时,那么将返回true;但是如果与''1.4.2'.或”.5"进行比较,那么将返回false。之所以得出这样的结论,是因为这种比较机制假设规范版本是向后兼容的。
实现的版本号没有规定的格式,因为提供实现的不同组织会对实现版本做不同的定义。在实现版本之间唯一能做的比较是测试版本是否相同,其中没有向下兼容的假设。
包可以被密封起来,这意味着不能再向这个包中添加类了。未密封的包可以包含来自类搜索路径中多个不同位置的类,而被密封的包的内容必须来自相同的位置—要么是某个特定的归档文件,要么是由某个URL指定的位置。有两种方法可以确定一个包是否被密封了:
.publicbooleanissealedp:如果该包被密封了,则返回trueo
.publicbooleanissealed(URLurl):如果该包对于给定的URL是密封的,则返回true,也就是说,该包中的类可以从这个给定的URL处加载。如果包中的类不能从给定的URL加载,或者包没有被密封,则返回false,包的规范和实现信息通常是作为与包存储在一起的清单文件的一部分而提供的—例如作为Java归档文件(jar)中的清单文件的一部分,就像25.9.2节“归档文件java.util.jar”中描述的那样。当加载包中类时,这些信息就会被读人。类加载器(ClassLoader)可以为它要加载的类动态地定义一个Package对象:
我们可以调用给定类的Class对象的getPackage方法来获得这个类的Package对象。我们也可以用给定的包名调用静态方Package.getPackage来获得Package对象,或者调用静态方Package.getPackages,它将返回由类加载器当前已知的所有包组成Package数组。这两个方法都与调用它们的代码的类加载器有关,因为这些代码将调用其类加载器的get-Package或getPackages方法。这些类加载器的方法将搜索特定的类加载器及其所有父类加载器,如果对当前类加载器没有做任何设置,那么此时就会使用系统类加载器。请注意,如果包未知,那么类加载器方法将返回null,因为此时还没有加载包中的任何类型。