4.2 Java包
Java语言程序是由一些类和接口组成的,为管理这些类和接口,Java体系制定了“包”管理机制,其主要功能是将用途相近但功能不同的一些类和接口集合在一起,或者是将一个完整的Java应用程序中的所有类和接口集合在一起。在同一个包内的所有类和接口不能有相同的名字,因此,包也是避免类和接口名字在同一个包内发生冲突的管理工具。
任何一种程序设计语言都会为其使用者提供类库、函数库等方便编写程序,Java也不例外。Java的应用程序类库是以包的形式提供的,功能和用途相近的一些类和接口被放在同一个包中,包的名字体现了其用途。
4.2.1 package语句
Java关键字package是用于定义包的,package字后跟包的名字,其语法格式为:
package语句圈定了一些类和接口是属于包名的,包名代表一些类和接口的集合,在同一个包名的集合中不能有相同的类或接口名,类与接口也不可同名。
包的名字是编程人员自己确定的,它是由单词构成,或是由圆点“.”操作符号分隔的多个单词组成的,单词是由英文小写字母组成。为包命名需要体现该名字的唯一性(非重名),因为包名的结构与互联网域名结构类似,因此,Java语言的发明者建议使用为互联网域名命名的规则为一个包命名,因为一个域名在互联网域中是唯一的。
package语句应放在Java编译单元(Java源代码文件.java)的第一行,并且只能有一条,package语句的功能为指明在Java编译单元内定义的所有类和接口是属于被命名的包中。
【示例4-10】 构成一个Java编译单元,其代码为:
上述程序的包名是bnu.sunyilin,要求在bnu范围内sunyilin是唯一的名字,OneClass类和OneInterface接口属于bnu.sunyilin包。
当多个Java编译单元共同使用一个包名时,每个编译单元的第一行package语句是一样的,则这些编译单元中的类和接口同属于一个包,例如下述两个编译单元程序:
上述两个编译单元的OneClass类和OneInterface接口属于bnu.sunyilin包。
在Java编译单元中可以省略package语句,没有package语句的Java编译单元中的类和接口是不属于任何包的,不属于任何包的类和接口是不能被其他Java程序所引用的。
当一些类和接口需要被其他应用程序所引用(包括访问和继承等操作)时,则需要将这些类和接口定义在包中,protected修饰的类和接口只能在包内(属于同一个包)的任何地方被引用,public修饰的类和接口可以在包内和包外(不属于同一个包)的任何地方被引用。
4.2.2 Java包与路径
Java的包实际上是一种Java类文件的组织和管理机制,包名字的构成是采用了树状结构的形式,树根与树叶是由圆点“.”操作符号分隔的,它正好与计算机操作系统对磁盘文件目录的树状管理是一致的,也就是包的管理结构与文件管理系统中的路径的层次结构是一致的。因此,包的名字是与类文件存放在磁盘中的路径(目录的树状结构)相关联的,包名中的圆点“.”分隔符确定的是包的层次结构,最前面的单词是Java包层次结构的根,分隔符后的单词为子包,根包为根目录,子包为子目录,Java类文件则需要存放在最后的子目录中。例如,包名为bnu.sunyilin,其类文件一定要存放在磁盘的..\bnu\sunyilin\路径下,在路径符中“..\”表示当前的工作路径,当磁盘符为F的根目录为工作路径时,“..\”为“F:\>”,再如包名为bnu.elec.syl,其类文件要存放在磁盘的..\bnu\elec\syl\路径下。包名中不含分隔符(一个单词)时,包名只表示根包,其对应的文件管理系统只有一层路径。
【示例4-11】 有package语句的Java应用程序,其程序代码为:
该程序文件以HelloWorld.java为文件名存放在磁盘的..\bnu\sunyilin\路径中,使用J2SDK工具在DOS命令提示符窗口中编译和运行该程序的命令为:
在编译和运行命令中,编译时需要指出HelloWorld.java文件存放的路径,运行时需要指出HelloWorld.class类文件属于的包,通过Windows的资源管理器查看Java文件如图4-3所示,在DOS命令提示符窗口中,上述J2SDK命令的操作是在F盘的根目录中进行的,而Java文件是存放在F:\>\bnu\sunyilin\路径中的,只有HelloWorld.class类文件存放在F:\>\bnu\sunyilin\路径下时,其通过J2SDK的java命令才能正确运行该程序。
图4-3 bnu.sunyilin包名对应的文件路径
4.2.3 import语句
Java体系提供了引用一个包中类和接口的机制,import就是用于指定导入一个包中类和接口到当前编译单元的Java关键字,import语句是针对具有package语句的编译单元而言的。import语句由import关键字后跟导入包中的类或接口名组成,当导入包中所有类和接口时,其所有类和接口的名字用“∗”符号代替。import语句使用的语法格式为:
import语句可以将指定包的公共(public)类和接口引入到当前的编译单元中,在编译单元内可以直接使用这些类和接口中的成员,成员包括被修饰为public的变量、常量、方法等。
在编译单元中import语句的位置是在package语句后面、第一个类或接口定义的前面。另外,在一个编译单元中可以出现任意数量的import语句。
【示例4-12】 在bnu.sunyilin包中定义的两个类,其程序代码为:
【示例4-13】 在bnu.elec.syl包中定义的一个ImportSample类,在该类中导入bnu. sunyilin包中定义的所有类。其程序代码为:
应当注意的是当上述程序在Windows操作系统中使用J2SDK命令编译和运行时,需要使用DOS的set命令将..\bnu\sunyilin\路径设置为公共的开放路径,或者需要设置Windows的环境变量,使其路径(PATH)或类路径(CLASSPATH)指向..\bnu\sunyilin\(设置步骤:右击“我的电脑”,选择“属性”→“系统属性”→“高级”选项,单击“环境变量”按钮,设置PATH和CLASSPATH内容,如图4-4所示),其目的有两个:一是当使用J2SDK的javac编译命令编译ImportSample类时,编译器可以找到..\bnu\sunyilin\目录下的OutPrintString和OutPrintInt类,将它们导入ImportSample类中以便使用;二是在使用J2SDK的java命令运行ImportSample程序时,Java运行环境JRE能够找到OutPrintString和OutPrintInt类。
图4-4 设置Windows环境变量
4.2.4 直接引用Java包中的类和接口
在Java体系中还可以用另外一种方式引用包中的类或接口到当前的编译单元,即直接引用法,它是针对包中的具体一个类或接口实现引用,直接引用包中的类或接口也被称为前缀包名法,它是指在Java应用程序中不使用import语句,使用包中具体类或接口时,在每个引用的类或接口前面给出它们所在包的名字,其语法格式为:
从packagename包中引入一个ClassName类在当前编译单元中创建一个objname对象,创建对象后则可使用该对象中的公共成员。
【示例4-14】 在bnu.elec.syl包中直接使用bnu.sunyilin包中定义的OutPrintString和OutPrintInt类(程序见4.2.3节),其前提是当在Windows操作系统中使用J2SDK命令编译和运行时,需要设置系统环境变量,使环境变量值PATH和CLASSPATH指向..\bnu\ sunyilin\路径。ImportSample类程序代码为:
4.2.5 Java包的应用
包确定了Java体系多个类的管理规则,对于存放于磁盘中的多个类文件,Java语言程序开发系统J2SDK同样提供了多个Java文件的管理工具——jar命令。该命令是将一些相关的类或是与应用程序有关的文件聚合(归档)为一个文件,它将这些文件以ZIP格式压缩到文件名后缀为.jar的文件中,方便文件的归类和管理,将一个包中定义的所有类文件压缩到∗.jar文件中提供给使用者的是一个理想的文件管理模式,因此,Java语言体系提供的类库都是以这种形式提供给编程人员的。例如,将..\bnu\目录中的所有文件压缩到Classes.jar文件中的行命令格式为:
另外,当一个Java应用程序被压缩到∗.jar文件中时,J2SDK的java命令也可以运行∗.jar文件格式的Java程序,但是在压缩应用程序的同时需要在∗.jar文件中建立一个“自述文件”,其最重要的目的就是指示应用程序的入口类(含有main()方法的类),自述文件为一个文本文件,其文件名为manifest.mf,在该文件中有指示应用程序入口类、版本等信息。
【示例4-15】 jar命令使用示例。一个Java应用程序HelloWorld类代码为:
其针对HelloWorld程序的manifest.mf文件的内容为:
提示:该文件需要以“回车”作为最后一行的结尾符,并以manifest.mf为文件名存放在当前磁盘的工作路径中。
使用J2SDK中jar命令,在Windows命令行窗口中生成含有manifest.mf自述文件的jar文件的命令格式为:
在Windows命令行窗口中,使用java命令运行HelloWorld.jar文件的命令格式为:
在Java应用程序和类库的开发与使用中,Java语言的发明者建议使用∗.jar文件形式为用户提供其应用程序和类库,在应用类库时则需要设置类库存放的路径(CLASSPATH),以便J2SDK中的命令能够找到类库。例如,javac编译命令可以找到被编译的Java程序中import导入的类。
Java语言为方便编程提供了大量的类库。类库是以包的形式提供给使用者的,其中最基础的类库是java.lang包,为支持Java应用程序的运行,java.lang包已经嵌入Java虚拟机中并创建为对象。因此,使用java.lang包中的类库时不需要import语句将该包导入,可以直接使用java.lang包中的类库。例如,输出显示文字的println()方法就是java.lang包的System类中定义的。
【示例4-16】 使用java.lang包Math类中方法的示例。该程序功能为通过勾股定理计算直角三角形斜边长度,两条直角边的长度是由随机数产生的,其程序代码为:
除了java.lang包外,当使用其他Java语言类库时则需要使用import语句导入被应用的包,例如下面的语句是导入实现输入、输出操作的java.io包。
一个类通过包被引入后,还可以通过继承的方式来应用它。
【示例4-17】 分别定义在三个不同包中的三个类,通过继承方式引用其他包中类的示例,定义在point包中的程序是一个在直角坐标系中描述点的Point类,其程序代码为:
定义在circle包中的程序是一个在直角坐标系中描述圆的Circle类,在直角坐标系中圆是由圆心和半径描述的,在point包中已经定义了Point点类,该点可作为描述圆心的坐标,在描述圆的类中只需要确定圆的半径即可,因此,Circle类是通过继承Point类确定圆心位置,所以需要引入point包中的Point类,其程序代码为:
上述程序当需要获取或设置圆心位置时,可通过Point父类的方法实现。
定义在user包中的程序是使用圆类(Circle类)创建一个圆对象circle的UseCircle类,通过import语句将Circle类导入该编译单元,其程序代码为: