在Java中,我们经常听到classpath这个东西,网上有很多关于“如何设置classpath”的文章,但是大部分设置都不靠谱。
到底什么是classpath?
classpath是JVM用到的一个环境变量,它用来指示JVM如何搜索class
因为Java是编译型语言,编码文件是.java,而编译后的.class文件才是真正可以被JVM执行的字节码。因此,JVM需要知道,如果要加载一个abc.xyz.Hello的类,应该去哪搜索对应的Hello.class文件。
所以,classpath就是一组目录的集合,它设置的搜索路径与操作系统相关。例如,在Windows系统上,用;做好事,带空格的目录用""括起来,可能长这样:

C:\work\project1\bin;C:\shared;"D:\My Documents\project1\bin"

在Linux系统上,用:分隔,可能长这样

/usr/shared:/usr/local/bin:/home/liaoxuefeng/bin

现在我们假设classpath.;C:\work\project1\bin;C:\shared,当JVM在加载abc.xyz.Hello时,会依次查找:

  • <当前目录>\abc\xyz\Hello.class
  • C:\work\project1\bin\abc\xyz\Hello.class
  • C:\shared\abc\xyz\Hello.class

注意到.代表当前目录。如果JVM在某个路径下找到了class文件,就不再继续搜索。如果所有路径下都没有找到,就报错。

classpath的设定方法有两种:

在系统环境变量中设置classpath环境变量,不推荐
在启动JVM时设置classpath变量,推荐。

我们强烈 不推荐 在系统环境变量中设置classpath,那样会污染整个系统环境。
在启动JVM时设置classpath才是推荐的做法,实际上就是给java命令传入 -classpath-cp参数:

  1. java -classpahth .;C:\work\project1\bin;c:\shared abc.xyz.Hello

或者使用-cp的简写

java -cp .;C:\work\project1\bin;c:\shared abc.xyz.Hello

没有设置系统环境变量,也没有传入-cp参数,那么JVMl默认的classpaht.,即当前目录。

java abc.xyz.Hello//只在当前目录搜索`Hello.class`

在IDE中运行Java程序,IDE自动传入的-cp参数是当前工程的bin目录和引入的jar包。
通常,我们在自己编写的class中,会引用 Java 核心库的class,例如StringArrayList等,这些class应该上哪去找?
有很多”如何设置classpath” 的文章会告诉你把JVM自带的rt.jar放入classpath,但事实上,根本不需要告诉JVM如何去Java核心库查找 class,JVM怎么可能笨到连自己的核心库在哪都不知道? :::danger 不要把任何Java核心库添加到classpath中!JVM根本不依赖classpath加载核心库! ::: 更好的做法是,不要设置classpath!,默认当前目录.对于绝大数情况都够用了。

假设我们有一个编译后的Hello.class,它的包名是com.example,当前目录是C:\work,那么,目录结构必须如下

运行这个Hello.class必须在当前目录下使用如下命令

C:/work> java -cp . com.example.Hello

JVM根据classpath设置的.在当前目录下查找 com.example.Hello,即实际搜索文件必须位于com/example/Hello.class。如果指定的.class文件不存在,或者目录结构和包名对不上,均会报错。

jar包

如果有很多.class文件,散落在各层目录中,肯定不便于管理。如果能把目录打一个包,变成一个文件,就方便多了。
jar包就是用来干这个事的,它可以把package组织的目录层级,以及各个目录下手所有文件(包括。出啦试试文件和其他文件)都打成一个jar文件,这样一来,无论是备份还是发给客户,就简单多了。<br />jar包实际就是一个zip格式的压缩文件,而jar包相当于目录。如果我们要执行一个jar包的class,就可以把jar包放到classpath`中

java -cp ./hello.jar abc.xyz.Hell

需要注意的是,jar包里的第一层目录不能是bin,而应该是hongmingmr

jar包还可以包含一个特殊的/META-INF/MANIFEST.MF文件,该文件是纯文本,可以指定 Main-Class和其它信息。JVM会自动读取这个MANIFEST.MF 如果存在Main-Class,我们就不必在命令行指定启动的类名,而是用更方便的命令

java -jar hello.jar

jar包里面还可以包含其它jar包,这个时候就需要在MANIFEST.MF文件里配置classpath了。

小结:
JVM通过环境变量classpath决定搜索class的路径和顺序
不推荐设备系统环境变量classpath,始终建议通过-cp命令传入
jar包相当于目录,可以包含很多.class文件,方便下载和使用。
MANIFEST.MF文件夹可以提供jar包的信息