5.1 在包中的类

img

扫码看视频

要将类放入一个包中,需要使用package关键字,后接包的名字,如代码5.1所示。

img

现在,Printer类就被包含在了“sunxin”包中。

要注意的是:

(1)package语句必须是文件中的第一条语句。也就是说,在package语句之前,除了空白和注释之外不能有任何语句。

(2)如果没有使用package语句,则指定为默认包(default package)或无名包。前面章节我们编写的所有类都在默认包中。

接下来在命令提示符窗口中编译并执行Printer类,如图5-2所示。

img

图5-2 编译并执行Printer类

可以看到,在执行Printer类时,出现了问题,提示“找不到或无法加载主类Printer”,这是怎么回事呢?

上面我们提到了包与文件夹之间的关系,实际上,Java中的包对应着文件系统的目录层次结构。现在Printer类在sunxin包中,那么编译后的Printer.class文件就应该放到sunxin文件夹下。既然这样,我们先建立一个名为sunxin的文件夹,然后把Printer.class文件移动到该文件夹下,最后进入sunxin文件夹下,执行Printer类,如图5-3所示。

img

图5-3 在包对应的文件夹中执行Printer类

从图5-3中可以看到,在sunxin文件夹下确实存在Printer.class文件,但为何还是找不到Printer类呢?有一句古诗说明了这种情况:“只缘身在此山中”。我们需要明确的是,包确实对应着文件系统的目录结构,但同时也是给类名添加了限定名,就如同C++和C#的namespace,简单来说,现在完整的类名是sunxin.Printer。

所以,我们应该先回到包名对应的文件夹的上一级目录中,然后执行sunxin.Printer,结果如图5-4所示。

img

图5-4 执行sunxin.Printer的输出结果

也可以使用“java sunxin/Printer”命令来执行Printer类,注意,包名称与类名称之间要使用斜杠“/”来分隔。不过,这种方式不常用。

在一个真实软件系统中,包名不会如此简单,通常会有多层嵌套,如果我们去查看Java的API文档,就会发现Java类库使用的包名称并不是简简单单的一个单词,而是像java.io、com.sun.management这种形式,这样的包名说明它是一个多层嵌套的包结构,对应了多级目录结构。

下面我们修改一下Printer类所在的包,在package语句中,用“.”来指明包(目录)的层次。如代码5.2所示。

img

当然,编译之后的Printer.class文件应该放到com\sunxin文件夹下。在执行时,要写上完整的类名com.sunxin.Printer,如图5-5所示。

img

图5-5 编译并执行com.sunxin.Printer类

要是每次编译完一个带包名的类,都要手动建立对应的目录层次结构,岂不是很麻烦?如果在编译的时候,能够让编译器自动根据包名帮助我们生成目录层次结构,岂不完美?这是可以做到的,Java编译器有这个功能。在命令提示符窗口中,输入javac并回车,就会列出javac命令的选项,其中有一个-d选项,如图5-6所示。

img

图5-6 javac命令的-d选项

-d选项用于指定在哪一个目录下生成字节码文件,该选项附加的作用就是会自动根据源程序中指定的包名建立对应的目录层次结构。

下面将先前生成的Printer.class文件所在的顶层文件夹com删除,在命令提示符窗口中执行下面的命令:

img

-d后面的“.”代表当前目录,也就是在当前目录下生成字节码文件,注意“.”前后的空格。在执行该命令后,你会欣喜地发现,Java编译器自动帮我们创建了与包层次结构对应的目录结构。

如果你想在其他位置存放生成的字节码文件,例如D盘,可以将“.”换成D:或D:\,如下所示:

img

这会在D盘根目录下生成包名对应的目录结构和Printer.class文件。

在第1.8.2节我们介绍过CLASSPATH环境变量,该变量用于设置Java类所在的路径,如果我们想执行D盘根目录的Printer类,那么该如何设置CLASSPATH变量的值呢?是直接指向Printer.class文件所在的文件夹,还是指向顶层包com所在文件夹(即D盘)呢?答案是后者,我们现在一定要明确Printer类的完整限定名是com.sunxin.Printer,所以你不要把包名对应的目录真的当成Windows系统的文件夹。

将当前目录下的com文件夹删除,在命令提示符窗口中设置CLASSPATH环境变量,指向D盘,执行com.sunxin.Printer类,如图5-7所示。

img

图5-7 执行D盘上的com.sunxin.Printer类

你可能想知道这个包应该如何命名,其实很简单,因为项目通常都是在公司开发的,大多数公司都是有域名的,可以先将域名作为包名,假定笔者有一个域名是:sunxin.com,那么将顶级域名com放前面,包名就是com.sunxin,也就是将平常习惯的域名书写顺序倒过来。之后,再将你开发的软件系统的名称作为子包名,例如笔者开发的是一个OA系统,那么包名就是com.sunxin.oa,其后的子包名再根据系统的模块划分,软件分层结构去设计包名。

有的读者可能会问,如果没有域名,该如何设计包名呢?很简单,可以用你名字的拼音作为包名啊,而且你是在学习阶段,包名如何取由你自己说了算。就如同这里的包名com.sunxin,就是作者随便取的。在此也强调一下,作者本人没有sunxin.com这个域名,不要试图通过这个域名去联系作者