Skip to content

Latest commit

 

History

History
332 lines (180 loc) · 217 KB

07-包.md

File metadata and controls

332 lines (180 loc) · 217 KB

第七章 包

程序组织成包的集合。每个包都有自己的类型的名称集合,这有助于防止名称冲突。

当且仅当类型声明为 public 时,顶层类型在声明其的包外是可访问的(6.6)。

包的命名结构是分层次的(7.1)。包的成员是,类和接口类型(7.6),它们声明在包的编译单元中,和子包,它们可能包含编译单元和它们自己的子包。

包可以存储在文件系统或数据库(7.2)中。存储在文件系统中的包可能在它们的编译单元的组织上具有某些约束,以允许一种简单的易于查找类的实现。

包由许多编译单元(7.3)组成。编译单元自动地访问声明在其包中所有类型,同时自动地导入声明在预定义的包 java.lang 中的所有 public 类型。

对于小程序和临时的开发来说,包可以是未命名的(7.4.2)或具有一个简单的名称,但如果代码要广泛分发,则应使用限定名称来选择唯一的包名称。这可以防止冲突的发生,如果两个开发组碰巧选择了相同的包名称,而这些包后来在同一个程序中使用。

7.1. 包成员

包的成员是其子包和在此包的所有编译单元中声明的所有顶层类类型(7.6,8(Classes))和顶层接口类型(9(Integfaces))。

avatar

    例如,在 Java SE 平台 API 中:

        包 java 具有子包 awt、applet、io、lang、net 和 util,但没有编译单元。

        包 java.awt 具有名为 image 的子包,还有许多包含类和接口类型声明的编译单元。

如果一个包的完全限定名称是 P,并且 Q 是 P 的一个子包,则 P.Q 是这个子包的完全限定名称,并再表示一个包。

包不可以包含两个具有相同名称的成员,否则产生一个编译时错误。

avatar

    下面是一些示例:

        由于包 java.awt 具有子包 image,因此它不能(且不)包含一个名为 image 的类或接口类型的声明。

        如果有一个名为 mouse 的包,并且该包中有一个成员类型 Button(它可能被引用为 mouse.Button),则不能有带有完全限定名称 mouse.Button 或 mouse.Button.Click 的任何包。

        如果 oom.nighthaoks.java.jag 是类型的完全限定名称,则不能有完全限定名称是 oom.nighthaoks.java.jag 或 oom.nighthaoks.java.jag.sorabble 的任何包。

包的分层次命名结构旨在方便地以常规方式组织相关的包,但除了禁止,一个包具有带有相同简单名称的子包作为该包中的顶层类型(7.6),以外,它本身没有任何意义。

例如,名为 oliver 的包和另一个名为 oliver.twist 的包之间或名为 evelyn.wood 和 evelyn.waugh 的包之间没有任何特殊的访问关系。即,名为 oliver.twist 的包中的代码不比任何其它包中的代码更易于访问包 oliver 内声明的类型。

7.2. 包的主机支持

每个主机系统决定如何创建和存储包和编译单元。

每个主机系统也决定在特定编译中哪个编译单元是可观察的(7.3)。编译单元的可观察性反过来决定哪个包是可观察的,哪个包在作用域内。

在 Java SE 平台的简单实现中,包和编译单元可能存储在本地文件系统中。其它实现可能使用分布式文件系统或某些数据库形式来存储它们。

如果主机系统将包和编译单元存储在数据库中,则数据库不能对基于文件的实现中允许的编译单元强加可选限制(7.6)。

例如,使用数据库存储包的系统不可以强制一个 public 类或接口一个编译单元的最大值。

但是,使用数据库的系统必须提供一个选项,以便将程序转换为服从限制的形式,以便导出基于文件的实现。

7.3. 编译单元

CompilationUnit 是 Java 程序的语法文法(2.3)的目标符号(2.1)。它由以下产生式定义:

CompilationUnit:
    [PackageDeclaration] {ImportDeclaration} {TypeDeclaration}

编译单元由三部分组成,每一部分是可选的:

    * 包声明(7.4),给定编译单元属于的包的完全限定名称(6.7)。

      没有包声明的编译单元是未命名包(7.4.2)的一部分。

    * 导入声明(7.5),其允许来自其它包的类型和使用简单名称引用的 static 类型成员。

    * 类和接口类型的顶层类型声明(7.6)。

每个编译单元隐式地导入在预定义包 java.lang 中声明的每个 public 类型名称,就像直接在任何 package 语句后的每个编译单元开始处出现的声明 import java.lang; 。因此,那些类型的名称在每个编译单元中都可用作简单名称。

预定义包 java 和其子包 lang 和 io 的所有编译单元总是可观察的。

对于所有其它包,主机系统决定哪些编译单元是可观察的。

编译单元的可观察性影响其包的可观察性(7.4.3)。

声明在不同编译单元中的类型可以循环地相互依赖。Java 编译期必须安排在同一时间编译所有此类类型。

7.4. 包声明

包声明出现在编译单元中,以指示此编译单元所属的包。

7.4.1 命名包

编译单元中的包声明指定编译单元所属的包名字(6.2)。

PackageDeclaration:
    {PackageModifier} package Identifier {. Identifier} ;

PackageModifier:
    Annotation

在包声明中提及的包名字必须是包的完全限定名(6.7)。

包声明的作用域和阴影在 6.3 和 6.4 中指定。

包声明上注解修饰符规则在 9.7.4 和 9.7.5 中指定。

对于给定的包,最多允许一个带注解的包声明。

强制执行这一限制的方式必须因实现而不同。对于基于文件系统的实现,强烈建议使用以下方案:如果存在单独带注解的包声明,则其放在名为 package-info.java 的源文件中,其在包含该包的源文件的目录中。此文件不包含名为 package-info.java 的类的源;实际上,这样做是非法的,因为 package-info 不是合法的标识符。通常 package-info.java 仅包含前面紧跟包上注解的包声明。虽然技术上该文件可以包含一个或多个包访问级别的类的源代码,但这是非常糟糕的形式。

如果存在 package-info.java,则建议用其代替 javadoc 和其它类似文档生成系统的 package.html。如果存在该文件,则文档生成工具应直接寻找在 package-info.java 中的(可能带有注解)包声明前面的包文档注释。通过这种方式,package-info.java 称为包级别的注解和文档的唯一仓库。将来如果想要添加任何其它包级别的信息,该文件将成为这些信息的方便的家。

7.4.2. 未命名包

没有包声明的编译单元是未命名包的一部分。

在开发小型或临时应用程序或刚开始开发时,Java SE 平台 tigong 未命名包,主要是为了方便。

未命名包不能具有子包,因为包声明语法总是包含到命名的顶层包的引用。

Java SE 平台的实现必须支持至少一个未命名包。实现可以支持多于一个未命名包,但不要求必须这样做。每个未命名包中的编译单元由主机系统确定。

avatar

在使用分层文件系统存储包的 Java SE 平台实现中,一个通常的策略是每个关联一个未命名包;一次只有一个未命名包是可观察的,即与“当前工作目录”关联的那个。“当前工作目录”的精确含义依赖于主机系统。

7.4.3. 包的可观察性

包是可观察的,当且仅当满足以下两者之一:

    * 包含包声明的编译单元是可观察的(7.3)。

    * 包的子包是可观察的。

包 java、java.lang 和 java.io 总是可观察的。

你可以从上面的规则和可观察的编译单元的规则得出这样的结论,如下所示。预定义的包 java.lang 声明了类 Object,因此 Object 的编译单元总是可观察的(7.3)。因此,java.lang 包是可观察的(7.4.3),java 包也是。此外,由于 Object 是可观察的,数组类型 Object[] 隐式存在。其父接口 java.io.Serializable(10.1)也存在,因此 java.io 包是可观察的。

7.5. 导入声明

导入声明允许通过由单个标识符组成的简单名称(6.2)来引用命名的类型或 static 成员。

不使用适当的导入声明,引用在另一个包中声明的类型或另一个类型的 static 成员的唯一方式是使用完全限定名称(6.7)。

ImportDeclaration:
    SingleTypeImportDeclaration
    TypeImportOnDemandDeclaration
    SingleStaticImportDeclaration
    StaticImportOnDemandDeclaration

    * 单类型导入声明(7.5.1)导入单个命名的类型,通过提到其规范名称(6.7)。

    * 按需类型导入声明(7.5.2)根据需要导入命名类型或命名包的所有可访问的类型(6.6),通过提到类型或包的规范名称。

    * 单静态导入声明(7.5.3)从类型导入所有可访问的给定名称的 static 成员,通过给定其规范名称。

按需静态导入声明(7.5.4)根据需要导入命名类型的所有可访问的 static 成员,通过提到类型的规范名称。

通过这些声明导入的类型或成员的作用域和遮蔽在 6.3 和 6.4 中指定。

导入声明仅在实际包含该导入声明的编译单元中使类型或成员可用其简单名称。通过导入声明引入的类型或成员的作用域不包括同一包中的其它编译单元、当前编译单元中的其它导入声明或当前编译单元中的包声明(包声明的注解除外)。

7.5.1. 单类型导入声明

单类型导入声明通过给定规范名称导入单个类型,使其可在出现该单类型导入声明的编译单元的类和接口声明中使用简单名称。

SingleTypeImportDeclaration:
    import TypeName ;

TypeName 必须是类类型、接口类型、枚举类型或注解类型(6.7)的规范名称。

名称必须是限定的(6.5.5.2),否则发生一个编译时错误。

如果命名类型是不可访问的(6.6),则这是一个编译时错误。

如果同一编译单元中的两个单类型导入声明尝试导入具有相同简单名称的类型,则发生一个编译时错误,除非这两个类型是相同的类型,这种情况下,将忽略重复的声明。

如果通过单类型导入声明导入的类型声明在包含此导入声明的编译单元中,则忽略导入声明。

如果单类型导入声明导入了一个简单名称是 n 的类型,并且编译单元也声明了一个简单名称是 n 的顶层类型(7.6),则发生一个编译时错误。

如果编译单元包含一个导入简单名称是 n 的类型的单类型导入声明,和一个导入简单名称是 n 的类型的单静态导入声明,则发生一个编译时错误。

avatar

avatar

avatar

avatar

7.5.2. 按需类型导入声明

按需类型导入声明允许根据需要导入命名包或类型的所有可访问类型。

TypeImportOnDemandDeclaration:
    import PackageOrTypeName . * ;

PackageOrTypeName 必须是包、类类型、接口类型、枚举类型或注解类型的规范名称(6.7)。

如果此 PackageOrTypeName 表示类型(6.5.4),则名称必须是限定的(6.5.5.2),否则发生一个编译时错误。

如果命名包或类型是不可访问的(6.6),则这是一个编译时错误。

在按需类型导入声明中命名 java.lang 或当前编译单元的命名包不是编译时错误。在这种情况下,将忽略按需类型导入声明。

同一编译单元中的两个或更多个按需类型导入声明可以命名相同的类型或包。除了这些声明中的一个以外的所有都被认为是多余的;效果就像仅导入该类型一次。

如果编译单元包含命名相同类型的按需类型导入声明和按需静态导入声明(7.5.4)两者,则效果就像仅导入该类型(8.5,9.5)的 static 成员类型一次。

avatar

7.5.3. 单静态导入声明

单静态导入声明从类型导入具有给定简单名称的所有可访问的 static 成员。这使得在出现该单静态导入声明的编译单元的类和接口声明中可以使用这些 static 成员简单名称。

SingleStaticImportDeclaration:
    import static TypeName . Identifier ;

TypeName 必须是类类型、接口类型、枚举类型或注解类型的规范名称(6.7)。

名称必须是限定的(6.5.5.2),否则发生一个编译时错误。

如果命名类型是不可访问的(6.6),则这是一个编译时错误。

Identifier 必须命名至少一个命名类型的 static 成员。如果没有该名称的 static 成员,或如果所有命名成员都是不可访问的,则这是一个编译时错误。

允许一个单静态导入声明导入多个具有相同名称的字段或类型,或多个具有相同名称和签名的方法。

如果单静态导入声明导入简单名称是 n 的类型,并且编译单元也声明了简单名称是 n 的顶层类型(7.6),则发生一个编译时错误。

如果编译单元包含一个导入简单名称是 n 的类型的单静态导入声明和一个导入简单名称是 n 的类型的单类型导入声明(7.5.1)两者,则发生一个编译时错误。

7.5.4. 按需静态导入声明

按需静态导入声明允许根据需要导入命名类型的所有可访问的 static 成员。

StaticImportOnDemandDeclaration:
    import static TypeName . * ;

TypeName 必须是类类型、接口类型、枚举类型或注解类型的规范名称(6.7)。

名称必须是限定的(6.5.5.2),否则发生一个编译时错误。

同一编译单元中的两个或更多个按需静态导入声明可以命名相同的类型;效果就像只有一个这样的声明。

同一编译单元中的两个或更多个按需静态导入声明可以命名相同的成员;效果就像仅导入该成员一次。

允许一个按需静态导入声明导入多个具有相同名称的字段或类型,或多个具有相同名称和签名的方法。

如果一个编译单元包含命名相同类型的一个按需静态导入声明和一个按需类型导入声明(7.5.2)两者,则效果就像仅导入该类型(8.5,9.5)的 static 成员类型一次。

7.6. 顶层类型声明

顶层类型声明声明一个顶层类类型(8(Classes))或一个顶层接口类型(9(Interfaces))。

TypeDeclaration:
    ClassDeclaration
    InterfaceDeclaration
    ;

额外的在编译单元的类型声明层次出现的 ";" 标记对编译单元的含义没有影响。在 Java 编程语言中允许独自的分号,仅作为对曾经在类声明之后放置 ";" 的 C++ 程序员的让步。不应在新的 Java 代码中使用它们。

缺少访问修饰符时,顶层类具有包访问:它仅在其被声明的(6.6.1)包的编译单元内是可访问的。类型可被声明为 public,以授予其它包中的代码访问该类型的权限。

如果一个顶层类型声明包含以下访问修饰符的任何一个:protected、private 或 static,则这是一个编译时错误。

如果一个顶层类型的名称表现为在同一包中声明的任何其它顶层类或接口类型的名称,则这是一个编译时错误。

作用域和顶层类型的遮蔽在 6.3 和 6.4 中指定。

顶层类型的完全限定名称在 6.7 中指定。

avatar

avatar

avatar

Java SE 平台的实现必须通过它们的二进制名称(13.1)跟踪包中的类型。必须将多种命名类型的方式扩展到二进制名称,以确保将此类名称理解为引用同一类型。

avatar

当且仅当包存储在文件系统中(7.2),主机系统可以选择强加一个约束,如果在文件中找不到类型名称加上扩展(如 .java 或 .jav)组成的名称的类型,则如果以下两者之一为 true,则这是一个编译时错误:

    * 由声明类型的包的其它编译单元中的代码引用的类型。

    * 声明为 public 的类型(因此可能从其它包中的代码访问)。

这一限制意味着每个编译单元必须最多有一个这样的类型。此限制使 Java 编译器可以轻松地在包中查找命名类。实际上,许多程序员选择将每个类或接口类型放在其自己的编译单元中,不管它是否是 public 或是被其它编译单元中的代码引用的。

avatar