Java程序运行机制及cmd编译运行探究(三) 测试类运行jar包
在Java程序运行机制及cmd编译运行探究(二) cmd编译运行Java程序并打成jar包中,我编写了一个生成随机数的工具类,编译过后,把字节码文件打成了2个jar包,放在d:\jar目录下。randomutil是正常打包,只包含了与class文件中package语句相匹配的文件结构,而randomutil2则把这个文件结构在本地磁盘上的路径也打包了进去。那么这两个jar包有什么区别?怎么使用这个jar包中的类及方法呢?以下,我编写一个测试类,来进行研究。在此过程中,如果遇到cmd命令不明白的,可以参看Java程序运行机制及cmd编译运行探究(一)准备工作。
一、编写测试类
//注意这里第一句是import语句,说明要引入其他类,而引入的类的全类名和被引入类的package语句保持一致
import pers.tony.utils.RandomUtils;
public class RandomTest
{
public static void main(String[] args)
{
//调用jar包中随机数的生成方法
System.out.println(RandomUtils.makeRandom());
}
}
保存源代码文件,命名为RandomTest.java,存储在d:\testclass目录下,如图所示:
注意代码第一句的import,说明需要引用其他类。
二、编译源代码
进入命令行,先输入正确的命令:javac -classpath d:\jar\randomutil.jar d:\testclass\RandomTest.java
可以看到测试类编译成功。简单解释下这个命令,意思是现在需要编译d:\testclass目录下的RandomTest文件,而编译这个类需要一些其他的类,比如这个类在开头import了一些其他类,那这些被引入的类去哪儿找呢?去d:\jar\randomutil.jar里面找,-classpath参数就是指定了类的搜索路径,如果不指定去哪里搜索这些被引用的类,编译就无法通过,如下图:
注意:
当需要引入的类是在jar包中时,-classpath参数的值一定要写jar包所在的路径加上jar包名,.jar后缀名也要写。
为什么一定要这么写呢?因为我们测试类import语句导入的是pers.tony.utils.RandomUtils的全类名,根据-classpath参数值,它会以d:\jar\randomutil.jar\pers\tony\utils这样的物理路径去找被引用的RandomUtils.class(即classpath的路径+import的路径),如果这里省略了路径或者路径不正确,比如.jar不写,在物理路径下自然找不到RandomUtils.class文件,测试类也就无法编译成功。
题外话:引用jar包中的类,-classpath参数需要把jar包的路径和名字写全,但如果直接引用的是class文件,则-classpath参数写到class文件所在目录就可以了。其实想想也很简单,上面我们写编译命令时classpath值也只写到jar包一层,没有写成javac -classpath d:\jar\randomutil.jar\pers\tony\utils\RandomUtils d:\testclass\RandomTest.java,因为pers\tony\utils\RandomUtils.class这部分的路径由源代码中的import语句代劳了。
三、运行字节码文件
在测试类编译过程中,由于用到其他类,我们使用-classpath参数指定了程序编译时查找相关类的路径,在运行测试类的时候同样需要指定。输入命令:java -classpath .;d:\jar\randomutil.jar RandomTest,可以看到调用jar包成功。
仔细看-classpath参数,和之前编译时候相比多了一个.;,其中点代表当前路径,分号代表分割符,分割了参数的值。
为什么需要为-classpath参数加上.这个值呢?因为在运行Java程序时,如果指定了-classpath参数,虚拟机会完全按照参数值去寻找当前类的,也就是说如果写成java -classpath d:\jar\randomutil.jar RandomTest,所有要用的类都在d:\jar\randomutil.jar下找,包括RandomTest类,可我们知道RandomTest类不在jar包下,而是当前路径,所以必须加上点。否则我要运行RandomTest类,结果连RandomTest类都找不到,效果如下:
好了,到此解决了jar包的调用问题,然而以上是根据randomutil包进行测试的,那上一篇中的把本地路径一起打成包的randomutil2到底能不能调用呢?
先用randomutil2.jar去编译RandomTest,输入命令javac -classpath d:\jar\randomutil2.jar d:\testclass\RandomTest.java
根据之前得到的结论:虚拟机根据classpath参数的路径+测试类import语句的路径去搜索所有相关类。这样写,虚拟机会以d:\jar\randomutil2.jar\pers\tony\utils目录去搜索RandomUtils.class,但我们知道之前randomutil2打包的时候,根目录是cmdtest,而现在命令中压根没有,这样的话肯定找不到RandomUtils.class,编译肯定过不了,试一下,果然失败!
想一想,上述命令失败的原因是因为寻找类的路径少了个cmdtest目录,那最有可能成功的就是想办法去补全cmdtest这个目录
有两种方法
1. 改命令,在jar包全路径后面加上cmdtest目录
javac -classpath d:\jar\randomutil2.jar\cmdtest d:\testclass\RandomTest.java
2. 修改源代码中的import语句
把import pers.tony.utils.RandomUtils;改成import cmdtest.pers.tony.utils.RandomUtils;
这两种方法至少从表面看,搜索相关类的路径是对的,可结果呢?都不行
比较麻烦就不贴图了,第一种我也不知道为什么,可能规定就是不能写jar包中的目录,可能是引用jar包有某种机制。第二种想一想,我import的是cmdtest.pers.tony.utils.RandomUtils类,可实际上RandomUtils类中的package语句是pers.tony.utils.RandomUtils啊,不匹配所以有问题,可能我的想法有偏差,欢迎指正。可不管怎么说,实际试下来打jar包时要把且只能把和类package语句结构一致的包结构进行打包,否则引用的时候会出现问题。