# 环境变量
```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提供不同的版本。