在日常写 Java 程序的工作中,运行,debug 等等操作,本质上都是在进行命令行操作。我们在 IDE 上点击运行,Java 程序就会运行,实际上 IDE 在背后帮我们进行了命令拼接。

我们可以在 IDEA 中编写一个简单的 Java 程序

package zzf;
 
/**
 * @author zzf
 * @date 2021/2/28/028 21:53
 */
public class Main {
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}

在 zzf 包下面有个 Main.java 类,里面有个 main 方法输出了 “Hello World”。在 IDEA 中点击了运行按钮之后,控制台会有一串命令行

D:\java\jdk\bin\java.exe -javaagent:D:\software\Idea\ideaIU-2019.2.3.win\lib\idea_rt.jar=56956:D:\software\Idea\ideaIU-2019.2.3.win\bin -Dfile.encoding=UTF-8 -classpath D:\Java\jdk\jre\lib\charsets.jar;D:\Java\jdk\jre\lib\deploy.jar;D:\Java\jdk\jre\lib\ext\access-bridge-64.jar;D:\Java\jdk\jre\lib\ext\cldrdata.jar;D:\Java\jdk\jre\lib\ext\dnsns.jar;D:\Java\jdk\jre\lib\ext\jaccess.jar;D:\Java\jdk\jre\lib\ext\jfxrt.jar;D:\Java\jdk\jre\lib\ext\localedata.jar;D:\Java\jdk\jre\lib\ext\nashorn.jar;D:\Java\jdk\jre\lib\ext\sunec.jar;D:\Java\jdk\jre\lib\ext\sunjce_provider.jar;D:\Java\jdk\jre\lib\ext\sunmscapi.jar;D:\Java\jdk\jre\lib\ext\sunpkcs11.jar;D:\Java\jdk\jre\lib\ext\zipfs.jar;D:\Java\jdk\jre\lib\javaws.jar;D:\Java\jdk\jre\lib\jce.jar;D:\Java\jdk\jre\lib\jfr.jar;D:\Java\jdk\jre\lib\jfxswt.jar;D:\Java\jdk\jre\lib\jsse.jar;D:\Java\jdk\jre\lib\management-agent.jar;D:\Java\jdk\jre\lib\plugin.jar;D:\Java\jdk\jre\lib\resources.jar;D:\Java\jdk\jre\lib\rt.jar;D:\idea-work\testDemo\out\production\testDemo zzf.Main

当我们把该命令复制到 CMD 窗口运行的时候,同样控制台会打印 “Hello World”

Java 执行过程

我们平时写的源代码(.java 文件),需要编译成字节码(.class 文件),然后再给到 jvm 去解释执行。jvm 只认识字节码,多个 class 文件可以打包成 jar 包来执行。

args 参数

我们写 Java 程序,main 方法里面都有一个 args 参数,实际上这个参数是可以从命令行获取的。编写以下程序获取打印 args 参数

public class Main {
	public static void main(String []args){
	System.out.println(java.util.Arrays.toString(args));
	}
}

使用如下命令编辑和运行该程序

image.png 可以看到使用 java Main 运行时,打印为空字符串,而在后面添加 1 2 3 三个参数时,打印 [1,2,3] 。

java Main 1 2 3 表示,调用 java 这个可执行程序,然后把 Main 1 2 3 这四个参数传递个 java。java 把 Main 这个类名拿出来,并且运行这个类,后面的参数 1 2 3 当做这个类的参数,传递给这个类的 main 方法。

jvm 中的系统属性和环境变量

系统属性和环境变量是完全不同的东西,系统属性只在 jvm 中有效,不像环境变量可以继承属性。

我们可以在代码中使用 System.getenv() 来获取当前环境变量,使用 System.getProperty() 获取系统属性。我们可以编写如下代码(其中 java.version 是 jvm 自带的系统属性)

public class Main {
	public static void main(String []args){
    // 打印 args 参数
	System.out.println("args: " +java.util.Arrays.toString(args));
    // 打印名为 zzf 的环境变量
	System.out.println("env: " +System.getenv("zzf"));
    // 打印名为 zzf 的系统属性
	System.out.println("System Property: " +System.getProperty("zzf"));
    // 打印名为 java.version 的系统属性
	System.out.println("Java version: " +System.getProperty("java.version"));
	}
}

我们先使用 export zzf=helo 给当前环境添加环境变量 zzf,值为 hello,然后运行。

image.png 可以发现系统属性 zzf 并没有,打印为 null。而由于 jvm 存在系统属性 java.version,所以正确打印出版本号。要传入系统属性给 jvm 我们需要使用 -D 参数,如图所示。

image.png

java 代码中的包

假设有如下代码 zzf.java:

import org.apache.commons.lang3.StringUtils;
public class zzf{
	public static void main (String[] args){
		System.out.println(StringUtils.isBlank(args[0]));
		System.out.println(StringUtils.isBlank(args[1]));
		System.out.println(StringUtils.isBlank(args[2]));
	}
}

该代码引用了第三方的包 org.apache.commons.lang3.StringUtils ,当我们使用 javac 编译的时候会报错,找不到这个包。因此,就需要指定这个包的目录,假设 commons-lang3-3.9.jar 文件和 zzf.java 文件在同一目录下,我们就可以使用 -classpath(简写 -cp),指定文件的位置。

javac -cp commons-lang3-3.9.jar zzf.java

这样就可以编译通过。但是在运行 java 程序的时候,需要同时指定引用的包的位置,还需要指定 zzf.class 文件位置

java -cp ./commons-lang3-3.9.jar:. zzf 1 2 3

该命令指定了第三方包的位置,以及 zzf.class 文件的位置。由于 zzf.class 文件是在当前目录,所以用 ”.” 来表示。在 Linux 中多个目录用 ”:” 隔开,Windows 使用 ”;” 隔开。

总结

回到这个命令行

D:\java\jdk\bin\java.exe -javaagent:D:\software\Idea\ideaIU-2019.2.3.win\lib\idea_rt.jar=56956:D:\software\Idea\ideaIU-2019.2.3.win\bin -Dfile.encoding=UTF-8 -classpath D:\Java\jdk\jre\lib\charsets.jar;D:\Java\jdk\jre\lib\deploy.jar;D:\Java\jdk\jre\lib\ext\access-bridge-64.jar;D:\Java\jdk\jre\lib\ext\cldrdata.jar;D:\Java\jdk\jre\lib\ext\dnsns.jar;D:\Java\jdk\jre\lib\ext\jaccess.jar;D:\Java\jdk\jre\lib\ext\jfxrt.jar;D:\Java\jdk\jre\lib\ext\localedata.jar;D:\Java\jdk\jre\lib\ext\nashorn.jar;D:\Java\jdk\jre\lib\ext\sunec.jar;D:\Java\jdk\jre\lib\ext\sunjce_provider.jar;D:\Java\jdk\jre\lib\ext\sunmscapi.jar;D:\Java\jdk\jre\lib\ext\sunpkcs11.jar;D:\Java\jdk\jre\lib\ext\zipfs.jar;D:\Java\jdk\jre\lib\javaws.jar;D:\Java\jdk\jre\lib\jce.jar;D:\Java\jdk\jre\lib\jfr.jar;D:\Java\jdk\jre\lib\jfxswt.jar;D:\Java\jdk\jre\lib\jsse.jar;D:\Java\jdk\jre\lib\management-agent.jar;D:\Java\jdk\jre\lib\plugin.jar;D:\Java\jdk\jre\lib\resources.jar;D:\Java\jdk\jre\lib\rt.jar;D:\idea-work\testDemo\out\production\testDemo zzf.Main

先不管 -javaagent ,从 -Dfile.encoding=UTF-8 给 jvm 设置一个系统属性 file.encoding=UTF-8,然后使用 -classpath 指定引用的包路径,要运行的文件的位置。最后只剩需要执行的文件名称 zzf.Main 。把上述这些都交给 D:\java\jdk\bin\java.exe 该可执行程序执行。