# 环境变量 ```java public class HelloWorld{ public static void main(String[] args){ System.out.println("Hello java!"); } } ``` 源文件只有一个类被public修饰,可存在多个类,多个类会生成多个字节码文件。执行各自的字节码文件。 # classpath 系统自动的隐藏环境变量,可配置任意路径到字节码文件。classpath为字节码文件路径配置的路径。 `classpath`是JVM用到的一个环境变量,它用来指示JVM如何搜索`class`。因为Java是编译型语言,源码文件是`.java`,而编译后的`.class`文件才是真正可以被JVM执行的字节码。因此,JVM需要知道,如果要加载一个`abc.xyz.Hello`的类,应该去哪搜索对应的`Hello.class`文件。 所以,`classpath`就是一组目录的集合,它设置的搜索路径与操作系统相关。 `classpath`的设定方法有两种: - 在系统环境变量中设置`classpath`环境变量,不推荐; - 在启动JVM时设置`classpath`变量,推荐。 ```bash java -classpath .;C:\work\project1\bin;C:\shared abc.xyz.Hello # -cp简写 java -cp .;C:\work\project1\bin;C:\shared abc.xyz.Hello ``` 不推荐在系统环境变量中设置`classpath`,那样会污染整个系统环境。在启动JVM时设置`classpath`才是推荐的做法。实际上就是给`java`命令传入`-classpath`或`-cp`参数。 没有设置系统环境变量,也没有传入`-cp`参数,那么JVM默认的`classpath`为`.`,即当前目录。在IDE中运行Java程序,IDE自动传入的`-cp`参数是当前工程的`bin`目录和引入的jar包。 不要把任何Java核心库添加到classpath中!JVM根本不依赖classpath加载核心库! # JAVA_HOME Windows 设置JAVA_HOME环境变量指向JDK安装目录。 ```bash Path=%JAVA_HOME%\bin ``` Linux ```bash #~/.bash_profile export JAVA_HOEM='/usr/libexec/java_home' export PATH=$JAVA_HOME/bin:$PATH ``` # JDK - java:运行可执行程序.java - javac:编译器 - jar:用于把一组.class文件打包成一个.jar文件 - javadoc:从java源码中提取注释文件并生成文档 - jdb:java调试器,用于开发阶段的运行调试 # jar包 如果有很多`.class`文件,散落在各层目录中,肯定不便于管理。如果能把目录打一个包,变成一个文件,就方便多了。jar包就是用来干这个事的,它可以把`package`组织的目录层级,以及各个目录下的所有文件(包括`.class`文件和其他文件)都打成一个jar文件,这样一来,无论是备份,还是发给客户,就简单多了。 jar包实际上就是一个zip格式的压缩文件,而jar包相当于目录。如果我们要执行一个jar包的`class`,就可以把jar包放到`classpath`中。 ```bash java -cp ./hello.jar abc.xyz.Hello ``` **创建jar包** 因为jar包就是zip包,所以,直接在资源管理器中,找到正确的目录,点击右键,在弹出的快捷菜单中选择“发送到”,“压缩(zipped)文件夹”,就制作了一个zip文件。然后,把后缀从`.zip`改为`.jar`,一个jar包就创建成功。 这里需要特别注意的是,jar包里的第一层目录,不能是`bin`,而应该是`hong`、`ming`、`mr`。原因是`hong.Person`必须按`hong/Person.class`存放,而不是`bin/hong/Person.class`。 jar包还可以包含一个特殊的`/META-INF/MANIFEST.MF`文件,`MANIFEST.MF`是纯文本,可以指定`Main-Class`和其它信息。JVM会自动读取这个`MANIFEST.MF`文件,如果存在`Main-Class`,我们就不必在命令行指定启动的类名,而是用更方便的命令。 ```java java -jar hello.jar ``` jar包还可以包含其它jar包,这个时候,就需要在`MANIFEST.MF`文件里配置`classpath`了。 在大型项目中,不可能手动编写`MANIFEST.MF`文件,再手动创建zip包。Java社区提供了大量的开源构建工具,例如Maven,可以非常方便地创建jar包。 在Java 9之前,一个大型Java程序会生成自己的jar文件,同时引用依赖的第三方jar文件,而JVM自带的Java标准库,实际上也是以jar文件形式存放的,这个文件叫`rt.jar`,一共有60多M。 如果是自己开发的程序,除了一个自己的`app.jar`以外,还需要一堆第三方的jar包,运行一个Java程序,一般来说,命令行写这样 ```bash java -cp app.jar:a.jar:b.jar:c.jar com.wang.Main ``` JVM自带的标准库rt.jar不要写到classpath中,写了反而会干扰JVM的正常运行。 如果漏写了某个运行时需要用到的jar,那么在运行期极有可能抛出`ClassNotFoundException`。 所以,jar只是用于存放class的容器,它并不关心class之间的依赖。 从Java 9开始引入的模块,主要是为了解决“依赖”这个问题。如果`a.jar`必须依赖另一个`b.jar`才能运行,那我们应该给`a.jar`加点说明啥的,让程序在编译和运行的时候能自动定位到`b.jar`,这种自带“依赖关系”的class容器就是模块。 为了表明Java模块化的决心,从Java 9开始,原有的Java标准库已经由一个单一巨大的`rt.jar`分拆成了几十个模块,这些模块以`.jmod`扩展名标识,可以在`$JAVA_HOME/jmods`目录下找到它们。这些`.jmod`文件每一个都是一个模块,模块名就是文件名。例如:模块`java.base`对应的文件就是`java.base.jmod`。模块之间的依赖关系已经被写入到模块内的`module-info.class`文件了。所有的模块都直接或间接地依赖`java.base`模块,只有`java.base`模块不依赖任何模块,它可以被看作是“根模块”,好比所有的类都是从`Object`直接或间接继承而来。 把一堆class封装为jar仅仅是一个打包的过程,而把一堆class封装为模块则不但需要打包,还需要写入依赖关系,并且还可以包含二进制代码(通常是JNI扩展)。此外,模块支持多版本,即在同一个模块中可以为不同的JVM提供不同的版本。