借如何编译learndiaryV0.9源码说说javac命令的基本用法

前两天,一位朋友在QQ上向我问一个问题,他说他下载了一个人力资源管理系统,可是不知道怎么编译它?

我答:一般的源码都有Readme文件或ant脚本,可以照此编译。。。

他回复说:里面的说明只有3行:数据库文件在data中,用户名**,密码**;其余什么都没有?

我答:您看它是用什么IDE开发的,最简单的方法是导入开发它的IDE环境中,如果没有,那就只有用javac命令手工编译了。。。

看到这,我给这个人力资源系统下了一个大概的结论,没有详细的文档,没有自动编译脚本,初学者难以把它编译成功。

恰好,我的"学习日记V0.9"也是这样一个“初学都难以编译成功”的程序。它是我初学JAVA完成的第一个成形的程序,可以运行,但是正由于是初学的实习之作。毛病不少。其中一个毛病就是源代码组织不合理,没有详细的文档,没有自动编译脚本。

于是,我们就只有用javac命令来手工编译这些程序了,关于javac命令,您用javac -help在线帮助可以看到全部的用法,只不过初学者可能对其中的含义不是很明白。我也是一个入门级的水平,对其中的全部用法也不是都用过和明白。所以,与其说我开展的是JAVA学习日记网站免费提供JAVA学习入门咨询服务,还不如说是借此机会和大家聊聊JAVA学习,共同交流、共同进步。下面我就借怎样编译成功learndiaryV0.9源码说说javac命令的基本用法,包括:-classpath -cp -sourcepath -d 各个选项。也算是对那位朋友的一个详细的解答。

1、您下载了learndiaryV0.9.rar并解开后,会看到以下文件清单:

  learndairydbV0.9.sql,数据库文件;learndiaryV0.9.war,编译好的程序文件;src,源代码文件目录;readme.html,使用说明。

  您按照里面readme.html文件的步骤应该能把这个程序运行起来(如果您不知道怎样导入数据库,请参考一下这篇日记:mysql数据库的导出和导入)。但是,初学者想修改src里面的源文件并编译运行只能用javac命令手工编译。而且,因为这个程序是用ultraedit手工写的,您想把它导入一个集成开发环境也不太容易。所以,我就用编译src里面的源码为例说说javac命令的基本用法;

2、我们把编译好的类文件放在哪里呢?

跟源文件放在一起肯定不是一个好办法,那么,在解开的目录中建一个名为build的文件夹,与src文件夹在同一目录下。build文件夹用于保存编译好的class文件;

3、让我们来初步试一试编译src里的源文件吧。(我用的是ubuntu 6.06 linux平台+jdk1.5,在windows平台下的路径表示有所不同,您需要改一下

learndiaryV0.9源码没有按包结构存放,如:src文件夹下面包含4个文件夹:action  actionform  dao  model  util,其中的一个类文件:LearnDiaryDB.java就直接放在dao文件夹下面,而LearnDiaryDB.java中的包结构是: package com.learndiary.website.dao;

标准的存放位置是./src/com/learndiary/website/dao。但是,这不妨害我们把源文件编译成类文件。

进入src文件夹所在的目录:


mdx@ubuntu:~/mdxhome/learndiaryV0.9.rar_FILES$ dir

build  learndiarydbV0.9.sql  learndiaryV0.9.war  readme.html  src

用javac命令编译src文件夹下的源文件到build文件夹下面:


mdx@ubuntu:~/mdxhome/learndiaryV0.9.rar_FILES$ javac -d ./build ./src/*/*.java ./src/*/*/*.java

结果如何呢?

长长的报错和警告,片断如下:


...(略)

./src/dao/LearnDiaryDB.java:290: 警告:编码 UTF8 的不可映射字符

      userInfo.setUserName("�ο�");

                            ^

...(略)

./src/actionform/ForbidForm.java:7: 软件包 org.apache.struts.action 不存在

import org.apache.struts.action.ActionErrors;

                                ^

./src/actionform/ForbidForm.java:23: 找不到符号

符号: 类 ActionForm

public class ForbidForm extends ActionForm {

                                ^

...(略)

./src/action/account/LoginAction.java:42: 找不到符号

符号: 类 ServletException

位置: 类 com.learndiary.website.action.account.LoginAction

                throws IOException, ServletException {

                                    ^

./src/action/account/LogoutAction.java:5: 软件包 javax.servlet 不存在

import javax.servlet.ServletException;

                     ^

...(略)

“警告:编码 UTF8 的不可映射字符”是怎么一回事呢?

原来learndiaryV0.9源程序里面有硬编码的中文字符,我用的是gb2312编码,而我现在的编译环境(ubuntu 6.06 linux)的默认编码是utf8,于是,需要指定encoding选项为: -encoding gb2312

而后面那些“找不到符号”、“软件包***不存在”的提示就是没有指定源码中引用到的其它类的路径了,这需要在classpath选项中指定,根据出错信息,我们为了方便,我们在当前目录下建一个名为compilelib的文件夹,把编译需要的库文件拷贝到这个文件夹下面,compilelib与src目录是同级的。确认编译需要的库文件可能不是一次尝试就能全部找出的,有时您需要根据出错信息从网上搜索,以判断是需要哪个库文件,一般来说,这些库文件在我下面说的两个地方都应该找得到,即:程序的WEB-INF/lib目录下面和web程序容器的通用库目录(如tomcat5.0是common/lib)下面。

到哪里去找哪些需要的库文件呢?

1)、用解压软件解开当前目录下面那个learndiaryV0.9.war文件,进入里面的那个WEB-INF/lib目录,把里面的activation.jar, mail.jar, struts.jar库文件拷贝到compilelib目录中;

2)、把您使用的tomcat(我用的是5.0版)安装目录下面common/lib下面的servlet-api.jar也拷贝到compilelib目录中;

于是,compilelib目录下面有如下库文件:


mdx@ubuntu:~/mdxhome/learndiaryV0.9.rar_FILES/compilelib$ dir

activation.jar  mail.jar  servlet-api.jar  struts.jar

然后,执行:


mdx@ubuntu:~/mdxhome/learndiaryV0.9.rar_FILES$ javac -classpath ./compilelib/struts.jar:./compilelib/servlet-api.jar:./compilelib/mail.jar:./compilelib/activation.jar -d ./build -encoding gb2312 ./src/*/*.java ./src/*/*/*.java

(windows下面的classpath分隔符是分号; ,而不是这里linux下面的冒号: )

再解释一下"./src/*/*.java"通配符,它表示编译src目录下面第一层所有目录中的所有java文件,而“./src/*/*/*.java”则表示编译src目录下面第二层所有目录中的所有java文件,learndiaryV0.9的源码只有两层深,如果碰到有的程序的源码有多层深,那您就多添几层了,我还没有找到手工用javac命令把一个目录下面所有目录层次的java文件在一句里面指定好一次全部编译的方法,有谁知道提示一下,谢谢:)

于是,按package组织好的类文件就被编译好放在build目录了,它的组织结构是这样的: ./build/com/learndiary/website/action/...

然后,您就可以把这些编译好的类拷贝到程序的WEB-INF/classes目录下面了。

按理说,我们已经成功的编译了learndiaryV0.9,那还需要我们做什么呢?

我们这里的目的是借编译learndiaryV0.9说说javac命令的用法,所以,我还得继续把字打下去:)

4、您会发现上面的-classpath的选项里没有当前目录. (点号),难道不需要当前目录吗?在javac命令里的-sourcepath选项与-classpath选项有什么区别呢?这两个问题我们以只编译./src/action/disgoal/DisGoalContentAction.java这个文件来说明。

首先,我们把整个源码根据包结构重新组织好,即在src目录下面建一个./src/com/learndiary/website/的目录结构(据个人经验,这样才能单独编译成功DisGoalContentAction.java),再把原来src目录下的那些文件夹移到website文件夹下面去,这样DisGoalContentAction.java的路径就是:./src/com/learndiary/website/action/disgoal/DisGoalContentAction.java

然后,我们回到src目录的上层目录,执行javac命令如下:


mdx@ubuntu:~/mdxhome/learndiaryV0.9.rar_FILES$ javac -classpath ./compilelib/struts.jar:./compilelib/servlet-api.jar:./compilelib/mail.jar:./compilelib/activation.jar -d ./build -encoding gb2312 ./src/com/learndiary/website/action/disgoal/DisGoalContentAction.java

但是,您会发现报错如下:


...(略)

./src/com/learndiary/website/action/disgoal/DisGoalContentAction.java:4: 软件包 com.learndiary.website.dao 不存在

import com.learndiary.website.dao.LearnDiaryDB;

                                  ^

./src/com/learndiary/website/action/disgoal/DisGoalContentAction.java:47: 找不到符号

符号: 类 LearnDiaryDB

...(略)

这是因为DisGoalContentAction.java中引用的用户源文件找不到,这时,我们的-sourcepath选项出场了,添加这个选项的命令如下:


mdx@ubuntu:~/mdxhome/learndiaryV0.9.rar_FILES$ javac -classpath ./compilelib/struts.jar:./compilelib/servlet-api.jar:./compilelib/mail.jar:./compilelib/activation.jar -sourcepath ./src -d ./build -encoding gb2312 ./src/com/learndiary/website/action/disgoal/DisGoalContentAction.java

一切OK了,并且,DisGoalContentAction.java引用到的几个源文件同时被编译进build目录了。

其中,“-sourcepath ./src”的作用是告诉javac到这个目录下面去找它要的东西。

实际上,-sourcepath这个选项完全可以被-classpath代替,只不过classpath除了可以找源文件,还可以找类文件,而sourcepath却只可以找源文件。也就是说,下面这个命令与上面的命令是等效的:


mdx@ubuntu:~/mdxhome/learndiaryV0.9.rar_FILES$ javac -classpath ./compilelib/struts.jar:./compilelib/servlet-api.jar:./compilelib/mail.jar:./compilelib/activation.jar:./src -d ./build -encoding gb2312 ./src/com/learndiary/website/action/disgoal/DisGoalContentAction.java

但是,反过来,用-sourcepath代替-classpath却不行,如下面的命令是不会执行成功的:


mdx@ubuntu:~/mdxhome/learndiaryV0.9.rar_FILES$ javac -sourcepath ./compilelib/struts.jar:./compilelib/servlet-api.jar:./compilelib/mail.jar:./compilelib/activation.jar:./src -d ./build -encoding gb2312 ./src/com/learndiary/website/action/disgoal/DisGoalContentAction.java

于是,我们再来说说classpath中当前路径 .  的作用。

假如您在当前目录下建了一个目录mypackage,再在mypackage下面写了一个Hello.java文件,并以mypackage为包,即它的头部有一句 package mypackage;

然后,您在当前目录(mypackage的父目录)下执行 javac ./mypackage/Hello.java,编译成功。在mypackage目录生成类文件Hello.class,然后,您在当前目录下执行:java -classpath test mypackage.Hello ,其中test是一个不存在的目录,目的是屏蔽系统的classpath中环境变量中定义的当前路径。您会发现类似下面的报错:


mdx@ubuntu:~$ java -classpath test mypackage.Hello

Exception in thread "main" java.lang.NoClassDefFoundError: mypackage/Hello

改过来,使当前路径生效:java -classpath . mypackage.Hello

执行结果就正常了。

所以,classpath当前路径 . 的作用就是告诉java从当前路径开始找它要的东西。

另外,javac中选项 -cp是选项-classpath的缩写,是完全等效的。

以上用编译learndiaryV0.9来说了javac命令的基本用法包括:-classpath -cp -sourcepath -d 各个选项。javac命令还有其它的选项,您用javac -help在线帮助可以看到,不过不常用,我也没有用过,就不写了。

5、一个疑问:

在我们编译DisGoalContentAction.java这一个java源文件的时候为什么必须要告诉它引用的其它源文件的搜索路径(用选项 -sourcepath ./src 告知),而前面编译learndiaryV0.9中src目录中全部源文件时却不用告诉它们彼此引用的其它源文件的搜索路径呢?而且,上面编译全部源文件时还并没有按包的结构组织源代码。大家可以探讨一下这是为什么?

今天就写到这里了。如您要转载本文请注明原始出处为:借如何编译learndiaryV0.9源码说说javac命令的基本用法: http://java.learndiary.com/diaries/3262.jsp 和作者信息: littlebat。我会在本文的原始出处随时对本文进行修定或补充。由于本人水平有限,错漏之处在所难免,欢迎您的批评指正和交流。谢谢。

                   JAVA学习日记  孟大兴

                                        

                                                         2007年5月28日早上初稿