- Java程序设计与应用开发(第3版)
- 吴敏 於东军 李千目主编 成维莉 邵杰 姜小花副主编
- 2529字
- 2025-03-22 04:39:52
4.3 包
包(package)是一组相关类和接口的集合,通常称为“类库”。Java语言提供了一些系统级基本包;程序员也可以自行定义应用系统的包,以存放相关的类和接口。包提供了命名空间管理和访问保护,包也为类复用提供了方便的途径。
4.3.1 包的作用
包的作用与其他编程语言中的函数库类似。它将实现某方面功能的一组类和接口集合为包进行发布。Java语言本身就是由一组包组成,每个包实现了某方面的功能。下面简单介绍一些常见Java系统包的作用。
•语言包(java.lang):提供的支持包括字符串处理、多线程处理、异常处理、数学函数处理等,可以用它简单地实现Java程序的运行平台。
•实用程序包(java.util):提供的支持包括散列表、堆栈、可变数组、时间和日期等。
•输入输出包(java.io):用统一的流模型来实现所有格式的I/O,包括文件系统、网络和输入。
•网络包(java.net):支持Internet的TCP/IP协议,用于实现Socket编程;提供了与Internet的接口,支持URL连接、WWW即时访问,并且简化了用户/服务器模型的程序设计。
•抽象图形用户接口包(javax.swing):实现了不同平台的计算机的图形用户接口部件,包括窗口、菜单、滚动条和对话框等,使得Java可以移植到不同的平台。
创建一个包的方法十分简单,只要将一个包的声明放在Java源程序的头部即可。包声明格式如下:
package packageName;
package语句的作用范围是整个源文件,而且同一个package声明可以放到多个源文件中,所有定义在这些源文件中的类和接口都属于这个包的成员。
如果我们准备开发一个自己的图形工具,就可以定义一个名叫Graphics的包,将所有相关的类放在这个包里。如下:
package Graphics; class Square{ ... } class Triangle{ ... } class Circle{ ... }
在一些小的或临时的应用程序中,我们可以忽略package声明,那么我们的类和接口被放在一个默认包(default package)中,默认包没有名称。
在前面的章节中我们曾提到package访问控制,只有声明为public的包成员才可以从一个包的外部进行访问。
4.3.2 包命名
包是实现某方面功能的程序集合,因此一个有意义的包名应该体现包的功能。另外,全球所有的Java程序员都在轰轰烈烈地开发自己的Java程序,命名自己的程序包,因此保证包名的唯一性也就成了一个问题。
各公司组织达成一个约定,在它们的包名称中使用自己的Internet域名的反序形式。例如常见的包名格式都是这样的:
com.company.package
这种方式可以有效地防止各公司组织之间在命名Java程序包上的冲突。在一个公司内部可能还会存在冲突,这需要公司内部的软件规范来解决,通常可以在公司名称后面增加项目的名称来解决。例如:
com.company.projectname.package
这种方式可以有效地确保Java程序包名的唯一性,但是包中的成员还是可能重名。例如在javax.swing包和java.util中都有一个类Timer,如果我们在同一段程序中同时引入了这两个包,那么下面这个语句就存在二义性:
Timer timer = new Timer();
在这种情况下,我们就需要采用类的完全限定名称来消除二义性。一个类的完全限定名称就是包含包名的类名。例如:
java.util.Timer timer = new java.util.Timer();
Java平台采用层次化的文件系统来管理Java源文件和类文件。Java包名称的每个部分对应一层子目录。例如下面是一个名为MyMath.java的Java源文件:
package edu.njust.cs; public class MyMath{ ... } class Helper{ ... }
该文件在文件系统(以Windows系统为例)中的存储位置为:
src\edu\njust\cs\MyMath.java
编译后,对应的类文件为:
classes\edu\njust\cs\MyMath.class classes\edu\njust\cs\Helper.class
其中,src和classes分别对应具体应用程序的源文件和类文件根目录,src目录和classes目录可以在一起,也可以各自独立。
在此顺便指出,在编译Java源代码时,编译器为一个源文件中定义的每个类和接口都创建一个单独的输出文件。输出文件的基本名称是类或接口名加上文件扩展名.class。
4.3.3 包的使用
一个包中的public类或public接口可以被包外代码访问;非public的类型则以包作为作用域,在同一包内可以访问,对外是隐藏的,甚至对于嵌套包也是隐藏的。
所谓嵌套包,是指一个包嵌套在另一个包中。例如javax.swing.event是一个包,同样javax.swing也是一个包,所以可以称javax.swing.event包嵌套在javax.swing包中。
当我们要使用某个包时,要通过关键字import实现:
import packagename;
比如:
//表示引入java.io包,.*表示java.io包中所有的类和接口 import java.io.*;
也可以指明只引入包中的某个类或是接口:
//表示只引入java.io包中的File类 import java.io.File;
在Java程序中,如果我们通过类的完全限定名称来使用一个类,可以省略import语句,不过这显然会给程序的书写带来诸多不便。
注意:在引入包时,并不会自动引入嵌套包中的类和接口,例如:
import java.swing.event.*;
只是表示引入包java.swing.event中的所有类和接口,但是包java.swing中的类和接口并不会被引入。
1. 使用系统提供的包
我们已经知道,系统提供了大量的类和接口供程序开发人员使用,并且按照功能的不同,存放在不同的包中。例如,如果在程序中需要用到一个接收用户输入的对话框,就可以使用javax.swing包中的JOptionPane类,如例4.5所示。
例4.5 DialogDemo.java
//引入包javax.swing中的JOptionPane类 import javax.swing.JOptionPane; public class DialogDemo{ public static void main(String []args){ String input=JOptionPane.showInputDialog("Please input text"); System.out.println(input); } }
程序运行结果如图4.1所示。

图4.1 程序运行结果
我们可以发现,需要使用包中的类或者接口时,总是需要先引入。读者可以将DialogDemo.java中的import语句注释掉,观察编译结果。
2. 使用自定义包
下面,我们来定义一个自己的数学类MyMath(其中只包含一个方法max),并将该类存放在包edu.njust.cs中。创建MyMath的类文件如下。
例4.6 MyMath.java
package edu.njust.cs; public class MyMath{ public static int max(int a,int b){ System.out.println("edu.njust.cs.MyMath's max() is called "); return a>b?a:b; } }
注意,程序的第一行使用了package语句,用于指定包名。此外,源文件MyMath.java必须存放在和包名一致的目录中,这里为edu\njust\cs。至于edu之上是否还包含目录无关紧要。为了说明问题,先将MyMath.java存放在d:\lib\edu\njust\cs目录中。然后,我们在d:\lib中开发了一个程序TestMyMath.java。
例4.7 TestMyMath.java
import edu.njust.cs.MyMath; public class TestMyMath{ public static void main(String []args){ int a=MyMath.max(100,200); System.out.println(a); } }
同样,在TestMyMath.java中需要引入包。编译并运行例4.7,结果如下:
edu.njust.cs.MyMath's max() is called 200
可以发现确实使用了我们自定义的包。
下面我们将TestMyMath.java存放到另一个目录下,例如c:\myprogram, MyMath.java的存放位置不变。进入c:\myprogram编译TestMyMath.java,会出现以下错误:
c:\myprogram>javac TestMyMath.java TestMyMath.java:1: cannot resolve symbol symbol : class MyMath location: package cs import edu.njust.cs.MyMath; ^ TestMyMath.java:4: cannot resolve symbol symbol : variable MyMath location: class TestMyMath int a=MyMath.max(100,200); ^ 2 errors
编译器提示找不到类MyMath。
为什么会是这样呢?当编译器在编译时,会自动在以下位置查找需要用到的类文件:
•当前目录。
•系统环境变量classpath指定的目录,称为类路径。
•JDK的运行库rt.jar,在JDK安装目录的jre\lib子目录中。
由于TestMyMath.java中用到了MyMath类,因此编译器会在上述3个位置搜索MyMath类文件。由于MyMath不在这3个位置的任何一个地方,所以编译器找不到MyMath的类文件,因而报错。可以使用两种方法来解决。
一种方法是在编译时指定类文件的搜索路径:
C:\myprogram>javac -classpath .;d:\lib TestMyMath.java
上面的命令中使用了参数-classpath来指定类文件的搜索路径。不同的搜索路径之间使用分号隔开。
另一种方法是直接设置系统的环境变量classpath,设置方法类似于path,参见第1章中的环境变量设定:在系统变量区域找到变量classpath(如果没有,则新建一个classpath变量),双击该行就可以编辑该环境变量的值。在该变量已有的值后追加“;d:\lib”(不包括引号)即可。