一篇文章让你秒懂Java运行基础

Javac编译

javac命令几个常用的选项

  • -classpath 指定依赖的class文件搜索路径
  • -sourcepath 指定依赖的源文件搜索路径
  • -d 指定编译后的class存放目录,这个目录必须提前建好
  • -extdirs 指定第三方依赖包,如jar、zip搜索目录
  • -encoding 指定源文件编码 如UTF-8

下面以简单的java项目进行操作说明,所有命令操作都在项目根目录D:\kingfsen\workspace\java-demo执行。

package引用

D:\kingfsen\workspace\java-demo>tree /F src/main/java
└─com
    └─kingfsen
        └─train
            ├─test
            │      Test.java
            │
            ├─test2
                   Test2.java

项目中有2个包,分别是test和test2,两个包下各有一个Java类,test2包下的Test2类引用了test包下的Test类。

public class Test2 {

    public static void main(String[] args) {
        Test t = new Test();
    }
}

要编译Test2,如果不使用-classpath-sourcepath,则需列出全部源代码文件进行编译。

javac -d target/class src/main/java/com/kingfsen/train/test2/*.java 
src/main/java/com/kingfsen/train/test/*.java

编译Test类最简单,不依赖任何包。

javac -d target/class src/main/java/com/kingfsen/train/test/*.java

直接编译Test2类,javac直接编译报错,找不到Test类,可通过-sourcepath指定源文件搜索路径。

javac -d target/class -sourcepath src/main/java 
src/main/java/com/kingfsen/train/test2/*.java

也可以通过-classpath指定依赖的class文件搜索路径,此时依赖的Test.class文件未生成,则将classpath当做sourcepath使用。

javac -d target/class -classpath src/main/java 
src/main/java/com/kingfsen/train/test2/*.java

jar引用

有如下java项目

src
  ├─main
  │  └─java
  │      └─com
  │          └─kingfsen
  │              └─train
  │                  │  App.java
  │                  │
  │                  └─util
  │                          ListUtil.java

App类引用了util包下的ListUtil类,而ListUtil类的getEmptyList方法引用了google的guava包

public class App {
    public static void main( String[] args ) {
        List<String> emptyList = ListUtil.getEmptyList();
        System.out.println("OK");
    }
}

现在编译App.java、ListUtil.java两个源代码文件

javac src/main/java/com/kingfsen/train/*.java src/main/java/com/kingfsen/train/util/*.java
src\main\java\com\kingfsen\train\util\ListUtil.java:3: 错误: 程序包com.google.common.collect不存在
import com.google.common.collect.Lists;
                                ^
src\main\java\com\kingfsen\train\util\ListUtil.java:14: 错误: 找不到符号
        return Lists.newArrayList();

编译时发生错误,找不到ListUtil中依赖的guava的jar包,通过-classpath指定依赖包文件路径则可编译成功。

javac -d target/class 
-classpath "D:\maven\respoistory\com\google\guava\guava\18.0\guava-18.0.jar"  
src/main/java/com/kingfsen/train/util/*.java

如果源代码依赖了多个jar包,jar包路径以;拼接

-classpath "D:\maven\respoistory\junit\junit\3.8.1\junit-3.8.1.jar;D:\maven\respoistory\com\google\guava\guava\18.0\guava-18.0.jar" 

依赖的三方jar包也可以通过-extdirs指定,它是个目录集合

javac -d target/class -extdirs "D:\maven\respoistory\com\google\guava\guava\18.0"  src/main/java/com/kingfsen/train/util/*.java

此时编译App.java,还需把依赖的util包下的List.class搜索路径src/main/java添加到classpath中

javac -d target/class -classpath "src/main/java;D:\maven\respoistory\junit\junit\3.8.1\junit-3.8.1.jar;D:\maven\respoistory\com\google\guava\guava\18.0\guava-18.0.jar"  src/main/java/com/kingfsen/train/App.java

通常一般通过-extdirs来指定一些扩展依赖jar、zip搜索路径,classpath来指定源文件或者class文件搜索路径, 一次要编译源文件太多,可以全部放在一个源文件清单中,如sourcefile.txt。

src/main/java/com/kingfsen/train/App.java
src/main/java/com/kingfsen/train/util/ListUtil.java
javac -d target/class -classpath src/main/java -extdirs "D:\maven\respoistory\junit\junit\3.8.1\junit-3.8.1.jar;D:\maven\respoistory\com\google\guava\guava\18.0" @sourcefile.txt

Java运行

在Java中,有两种常用的运行方式,直接运行主体类main方法,或者java -jar xxx.jar。

MainClass

通过-classpath指定所有的class文件搜索路径,包括javac生成的字节码目录target/class。

java -classpath "D:\maven\respoistory\junit\junit\3.8.1\junit-3.8.1.jar;D:\maven\respoistory\com\google\guava\guava\18.0\guava-18.0.jar;target/class" com.kingfsen.train.App

Jar

打普通jar包

jar cf demo.jar target/class/*

查看jar包中文件清单

jar tfv demo.jar

解压jar包

jar xf demo.jar
 0 Mon Jul 03 11:42:30 CST 2023 META-INF/
69 Mon Jul 03 11:42:30 CST 2023 META-INF/MANIFEST.MF
 0 Mon Jul 03 10:24:54 CST 2023 target/class/com/
 0 Mon Jul 03 10:24:54 CST 2023 target/class/com/kingfsen/
 0 Mon Jul 03 10:41:48 CST 2023 target/class/com/kingfsen/train/
507 Mon Jul 03 11:00:52 CST 2023 target/class/com/kingfsen/train/App.class
 0 Mon Jul 03 10:24:54 CST 2023 target/class/com/kingfsen/train/test/
203 Mon Jul 03 11:00:52 CST 2023 target/class/com/kingfsen/train/test/Test.class
 0 Mon Jul 03 10:26:26 CST 2023 target/class/com/kingfsen/train/test2/
325 Mon Jul 03 11:00:52 CST 2023 target/class/com/kingfsen/train/test2/Test2.class
 0 Mon Jul 03 10:39:34 CST 2023 target/class/com/kingfsen/train/util/
438 Mon Jul 03 11:00:52 CST 2023 target/class/com/kingfsen/train/util/ListUtil.class

打Java可执行包时,需要提前准备一个Manifest内容的txt文件

Manifest-Version: 1.0
Created-By: kingfsen
Main-Class: com.kingfsen.train.App

通过jar cfm打可执行包

cd target/class
jar cfm demo.jar D:/kingfsen/workspace/java-demo/manifest.txt .

注意可执行jar中的目录必须是包的起始子目录com,而不是target/class,此时可通过java的扩展加载器加载依赖的第三方jar,使用-Djava.ext.dirs

java -Djava.ext.dirs="$JAVA_HOME/jre/lib/ext;D:\maven\respoistory\junit\junit\3.8.1;D:\maven\respoistory\com\google\guava\guava\18.0" -jar demo.jar

也可以通过根加载器加载第三方jar,使用-Xbootclasspath/a

java -Xbootclasspath/a:"D:\maven\respoistory\junit\junit\3.8.1\junit-3.8.1.jar;D:\maven\respoistory\com\google\guava\guava\18.0\guava-18.0.jar" -jar demo.jar

java -jar xxx.jar命令运行时,classpath被自动设置为xxx.jar,此时命令行使用-classpath无效,如需通过应用加载器加载第三方jar,需要在manifest中将依赖包添加到classpath中。 MANIFEST.MF中的Class-Path只能使用\,不能出现任何反斜杠,也不能出现绝对路径。

执行 jar -jar demo.jar是,只能以demo.jar路径起始定义Class-Path, 多个jar以空格隔开,通常新建一个目录lib,将jar都copy到lib目录中。

manifest文件

Manifest-Version: 1.0
Created-By: kingfsen
Class-Path: lib/mysql-connector-j-8.0.31.jar lib/guava-18.0.jar xx.jar
Main-Class: com.kingfsen.train.App

打包运行

jar cfm demo.jar D:/kingfsen/workspace/java-demo/manifest.txt .
java -jar demo.jar

除了使用java提供的二进制命令打包,还可以直接使用Java中API进行打包,可参阅JarOutputStream源代码。

java打包命令参考:https://docs.oracle.com/en/java/javase/17/docs/specs/man/java.html