Java的两种运行方式
先介绍Java的两种运行方式:
1.Java -jar ***.jar
这种方式必须在jar包中的MANIFEST.MF文件中指定所有运行该jar所需的所有依赖包和资源,例如:Main-Class, Class-Path等。
当使用-jar的方式运行时,会忽略掉参数-classpath和环境变量中classpath,所以在这种方式下通过这两种方式来指定classpath是没有效果的,只有MANIFEST.MF文件中Class-Path才有效。
2.Java -classpath [jar包路径;其它需要加装的路径] com.***.***.MainClass
使用这种方式运行Java的时候,会在classpath(可以通过命令行中-classpath指定,也可以通过设置环境变量classpath指定)路径下寻找命令行中指定的类(main函数所在类)进行启动。
classpath的路径组织方式
在classpath中可以指定具体的jar包路径和在程序中需要用到的资源目录路径。例如:classpath=D:\ClassLoaderResourceTest.jar;D:\test2;D:\test。如果需要指定多个jar包,可以使用统配符,例如:D:\JarPackage\*,这样就可以加载所有需要的jar包了(当使用这种方式的时候,*并不匹配除了jar包文件外的其它东西,例如:D:\JarPackage\这个目录下有另外的资源文件,则D:\JarPackage\*这个方式只会加载该目录下的jar包,其它资源文件是无法加载进来的)。当给classpath指定jar包或者目录路径的时候,相当于把jar包和目录路径都挂载到了classpath的根目录下,从而形成其classpath内部的目录结构。从表象上来看,跟Linux系统的挂载类似。
举个例子:
设置classpath为D:\ClassLoaderResourceTest.jar;D:\test2;D:\test,其中ClassLoaderResourceTest.jar目录结构如下:
![]()
D:\test2的目录下面只有一个f.txt文件,D:\test目录下面有一个d.txt文件和one目录,one目录下有一个e.txt文件。
所以这个classpath的目录结构如下:
![]()
当Java运行时从classpath中获取资源的时候,将遵循这个目录结构来获取所需要的资源,所以当从classpath获取不到需要的资源的时候,需要好好理解一下classpath的目录组织结构。
classpath指定jar包所在路径,却加载不到类:
在通过classpath指定jar包的时候,会有人只指定路径,而没有指定具体jar包,从而导致类加载不到。其实表象的原理是和Linux的挂载完全一样的,当指定一个目录挂载的时候,该目录下的jar包只是被当做了一个普通的文件,而该文件内容对于JVM来说是未知的,所以加载类的时候,找不到包里面的class文件。当指定具体jar包的时候,就跟在Linux上面挂载一个镜像文件一样,会展开里面的目录结构,相当于把包里面的整个目录结构都挂载进来了,就如同上面表格一样,所以ClassLoader就能按照类名加载到所需要的class文件了。
getResource的使用介绍
这边介绍两种getResource的区别和使用,在Class类中和ClassLoader类中分别存在一个getResource方法,这两个方法都是从classpath中寻找指定资源并获取资源的绝对路径,供后续加载资源用。
区别在于从classpath中加载资源时候的路径不一样。
Class类中的getResource方法,存在相对路径和绝对路径两种。
相对路径: 当使用相对路径时,将从该类所在的路径下面开始定位资源,也就是相对于该类所在位置的目录。例如:ClassLoaderResourceTest.class.getResource("a.txt"),从上面的classpath目录组织结构可以看出,ClassLoaderResourceTest类与a.txt处在同个目录下;如果要取b.txt资源,则ClassLoaderResourceTest.class.getResource("conf/b.txt")。如果需要去位于该类所在目录的父目录的资源,无法跟Linux文件系统一样,通过../来定位资源,而是需要使用绝对路径的方式来定位。
绝对路径: 就是在classpath的根目录下定位文件,和Linux一样,classpath的目录组织结构也是通过/来表示根目录。所以如果需要定位c.txt文件,就可以通过ClassLoaderResourceTest.class.getResource("/c.txt")来定位。
通过相对路径和绝对路径的配合,可以取到classpath中的所有资源。
ClassLoader中的getResource方法,只存在相对路径。
为什么这个方法会只有一个相对路径的方式呢,原因就在于它的起始路径在classpath的根目录下,所以只需要使用相对路径,就能取到classpath下的所有资源了。例如:ClassLoaderResourceTest.class.getClassLoader().getResource("c.txt"),ClassLoaderResourceTest.class.getClassLoader() .getResource("com/cyc/classloader/test/a.txt")。
示例
如下示例演示了classpath的目录组织结构和getResource的两种不同使用:package com.cyc.classloader.test; public class ClassLoaderResourceTest { public static void main(String[] args) { //获取与ClassLoaderResourceTest同目录下资源。 //输出结果:jar:file:/D:/ClassLoaderResourceTest.jar!/com/cyc/classloader/test/a.txt System.out.println(ClassLoaderResourceTest.class.getResource("a.txt")); //获取与ClassLoaderResourceTest同目录下资源。 //输出结果:jar:file:/D:/ClassLoaderResourceTest.jar!/com/cyc/classloader/test/conf/b.txt System.out.println(ClassLoaderResourceTest.class.getResource("conf/b.txt")); //获取与ClassLoaderResourceTest父目录下资源,使用相对路径无法获取,不支持../ //输出结果:null System.out.println(ClassLoaderResourceTest.class.getResource("../g.txt")); //与ClassLoaderResourceTest类不处于同目录,路径不对 //输出结果:null System.out.println(ClassLoaderResourceTest.class.getResource("c.txt")); //使用绝对路径,从根目录下获取 //输出结果:jar:file:/D:/ClassLoaderResourceTest.jar!/c.txt System.out.println(ClassLoaderResourceTest.class.getResource("/c.txt")); //使用绝对路径,获取根目录下的子目录文件,该目录是属于test,不属于jar包 //输出结果:file:/D:/test/one/e.txt System.out.println(ClassLoaderResourceTest.class.getResource("/one/e.txt")); //使用相对路径,获取jar包中的文件,该文件处于classpath的根目录下 //输出结果:jar:file:/D:/ClassLoaderResourceTest.jar!/c.txt System.out.println(ClassLoaderResourceTest.class.getClassLoader().getResource("c.txt")); //使用相对路径,获取test目录中的文件,该文件处于classpath的根目录下 //输出结果:file:/D:/test/d.txt System.out.println(ClassLoaderResourceTest.class.getClassLoader().getResource("d.txt")); //使用绝对路径,但是ClassLoader中的方法不支持绝对路径。 //输出结果:null System.out.println(ClassLoaderResourceTest.class.getClassLoader().getResource("/")); } }
运行命令如下:java -classpath D:\ClassLoaderResourceTest.jar;D:\test2;D:\test com.cyc.classloader.test.ClassLoaderResourceTest
输出结果如下:jar:file:/D:/ClassLoaderResourceTest.jar!/com/cyc/classloader/test/a.txt jar:file:/D:/ClassLoaderResourceTest.jar!/com/cyc/classloader/test/conf/b.txt null null jar:file:/D:/ClassLoaderResourceTest.jar!/c.txt file:/D:/test/one/e.txt jar:file:/D:/ClassLoaderResourceTest.jar!/c.txt file:/D:/test/d.txt null