目录

Java命令简易入门-2javac与java命令之一javac

Java命令简易入门-2:javac与java命令之一(javac)

Java命令简易入门2-Javac与Java命令之一

文章目录

基本概念

javac与java命令是我们最常用的Java命令。

javac :Java编译器。负责编译,将.java这个文本文件编译成.class字节码文件。

java :Java程序启动器。负责启动Java虚拟机(JVM)以运行Java程序。其主要用来载入字节码文件中的主类、执行jar文件等。

我们可以通过 javac --helpjava -help 来查看其帮助。

执行 javac -help 显示: 用法: javac

其中的 source files 指的是源代码文件。比如 javac HelloWorld.java ,生成HelloWorld.class。里面的主类是 HelloWorld

其中的 options 指的是可选的其他参数。

执行 java -help 显示: 用法:java [options] <主类> [args…]

可以看出,其必要参数是 主类 。注意,是 主类而不是文件 。因此正确执行命令是 java HelloWorld 而不是 java HelloWorld.class 。因为HelloWorld才是主类。

我们会通过几个实验来简单讲解一下javac与java的基本用法。

实验环境与实验文件

OS:Windows 10。

JDK:AdoptOpenJDK的jdk-11.0.8.10-hotspot

假设有 d:\test\ 目录下有HelloWorld.java,源代码如下:

public class HelloWorld{
    public static void main(String[] args) {
        System.out.println("HelloWorld!");
    }
}

这是一个很单纯的.java文件,里面包含一个主类HelloWorld。我们将在这个代码的基础上逐步扩展,一步步讲解javac与java命令。

1.javac与java基本用法

执行如下命令:

javac -version
java -version
javac HelloWorld.java
java HelloWorld

说明:

  1. 前两个命令用来查看当前命令行下执行的javac与java是什么版本的。
  2. 编译 当前目录下 的HelloWorld.java文件并在 当前路径下 生成 HelloWorld.class
  3. 启动JVM载入HelloWorld这个主类,从而运行该Java程序。再次强调一下java HelloWorld中的HelloWorld是类名而不是文件名。

注意:一个系统上可能有多个javac与java命令文件。可以使用 where javacwhere java 查看系统中的相应的文件。

命令输出截图如下:

https://i-blog.csdnimg.cn/blog_migrate/d4d4c50429a81c871465ef6cc3581e54.png

2. javac的其他常用参数

这里只介绍几个常用的参数。

-d <directory> :在指定目录(directory)下生成相应的.class文件

-verbose :输出有关编译器正在执行的操作的消息。

现在想将编译生成的.class文件自动放在d:\testjava下,如何实现呢?

del *.class
del d:\testjava*.class
javac -d d:\testjava HelloWorld.java
dir *.class
dir d:\testjava*.class

说明:

  1. del *.class 将当前目录下所有.class文件删除, del d:\testjava*.classd:\testjava\ 目录下所有.class文件删除。
  2. 编译当前目录下的HelloWorld.java并将生成的.class文件放到d:\testjava目录。注意:d:\testjava可以不用事先建立,该条命令会自动建立。
  3. dir *.class 查看当前目录下的.class文件,发现什么都没有
  4. dir d:\testjava*.class ,查看d:\testjava\目录下的所有.class文件。

命令输出截图如下:

https://i-blog.csdnimg.cn/blog_migrate/bf33ec271d8093e7bf359a0565a9dcc4.png

再来看看 -verbose 参数

javac -verbose HelloWorld.java

输出信息如下:

D:\test>javac -verbose HelloWorld.java
[语法分析开始时间 SimpleFileObject[D:\test\HelloWorld.java]]
[语法分析已完成, 用时 12 毫秒]
[正在加载/modules/jdk.internal.jvmstat/module-info.class]
[正在加载/modules/java.security.jgss/module-info.class]
[正在加载/modules/jdk.internal.ed/module-info.class]
[正在加载/modules/java.xml.crypto/module-info.class]
[正在加载/modules/java.base/module-info.class]
[正在加载/modules/java.scripting/module-info.class]
[正在加载/modules/jdk.security.jgss/module-info.class]
[正在加载/modules/java.rmi/module-info.class]
[正在加载/modules/jdk.jfr/module-info.class]
[正在加载/modules/jdk.dynalink/module-info.class]
[正在加载/modules/jdk.security.auth/module-info.class]
[正在加载/modules/java.desktop/module-info.class]
[正在加载/modules/jdk.unsupported/module-info.class]
[正在加载/modules/jdk.internal.opt/module-info.class]
[正在加载/modules/jdk.unsupported.desktop/module-info.class]
[正在加载/modules/java.instrument/module-info.class]
[正在加载/modules/jdk.jcmd/module-info.class]
[正在加载/modules/jdk.jlink/module-info.class]
[正在加载/modules/jdk.xml.dom/module-info.class]
[正在加载/modules/jdk.jconsole/module-info.class]
[正在加载/modules/jdk.jstatd/module-info.class]
[正在加载/modules/jdk.compiler/module-info.class]
[正在加载/modules/jdk.internal.vm.ci/module-info.class]
[正在加载/modules/jdk.jartool/module-info.class]
[正在加载/modules/java.se/module-info.class]
[正在加载/modules/jdk.internal.vm.compiler/module-info.class]
[正在加载/modules/jdk.internal.le/module-info.class]
[正在加载/modules/jdk.charsets/module-info.class]
[正在加载/modules/java.net.http/module-info.class]
[正在加载/modules/java.xml/module-info.class]
[正在加载/modules/jdk.pack/module-info.class]
[正在加载/modules/jdk.jsobject/module-info.class]
[正在加载/modules/jdk.naming.rmi/module-info.class]
[正在加载/modules/jdk.sctp/module-info.class]
[正在加载/modules/jdk.jshell/module-info.class]
[正在加载/modules/jdk.naming.dns/module-info.class]
[正在加载/modules/jdk.net/module-info.class]
[正在加载/modules/jdk.scripting.nashorn.shell/module-info.class]
[正在加载/modules/java.datatransfer/module-info.class]
[正在加载/modules/jdk.management/module-info.class]
[正在加载/modules/jdk.httpserver/module-info.class]
[正在加载/modules/java.sql.rowset/module-info.class]
[正在加载/modules/java.management/module-info.class]
[正在加载/modules/jdk.crypto.cryptoki/module-info.class]
[正在加载/modules/java.management.rmi/module-info.class]
[正在加载/modules/java.transaction.xa/module-info.class]
[正在加载/modules/jdk.jdi/module-info.class]
[正在加载/modules/jdk.management.agent/module-info.class]
[正在加载/modules/java.security.sasl/module-info.class]
[正在加载/modules/java.naming/module-info.class]
[正在加载/modules/jdk.javadoc/module-info.class]
[正在加载/modules/jdk.accessibility/module-info.class]
[正在加载/modules/jdk.scripting.nashorn/module-info.class]
[正在加载/modules/jdk.internal.vm.compiler.management/module-info.class]
[正在加载/modules/java.prefs/module-info.class]
[正在加载/modules/java.sql/module-info.class]
[正在加载/modules/jdk.editpad/module-info.class]
[正在加载/modules/jdk.jdwp.agent/module-info.class]
[正在加载/modules/java.smartcardio/module-info.class]
[正在加载/modules/jdk.attach/module-info.class]
[正在加载/modules/jdk.zipfs/module-info.class]
[正在加载/modules/jdk.jdeps/module-info.class]
[正在加载/modules/jdk.rmic/module-info.class]
[正在加载/modules/jdk.aot/module-info.class]
[正在加载/modules/jdk.localedata/module-info.class]
[正在加载/modules/jdk.crypto.ec/module-info.class]
[正在加载/modules/jdk.crypto.mscapi/module-info.class]
[正在加载/modules/java.logging/module-info.class]
[正在加载/modules/java.compiler/module-info.class]
[正在加载/modules/jdk.management.jfr/module-info.class]
[正在加载/modules/jdk.hotspot.agent/module-info.class]
[源文件的搜索路径: .]
[类文件的搜索路径: C:\Program Files\AdoptOpenJDK\jdk-11.0.8.10-hotspot\lib\modules,.]
[正在加载/modules/java.base/java/lang/Object.class]
[正在加载/modules/java.base/java/lang/String.class]
[正在加载/modules/java.base/java/lang/Deprecated.class]
[正在加载/modules/java.base/java/lang/annotation/Retention.class]
[正在加载/modules/java.base/java/lang/annotation/RetentionPolicy.class]
[正在加载/modules/java.base/java/lang/annotation/Target.class]
[正在加载/modules/java.base/java/lang/annotation/ElementType.class]
[正在检查HelloWorld]
[正在加载/modules/java.base/java/io/Serializable.class]
[正在加载/modules/java.base/java/lang/AutoCloseable.class]
[正在加载/modules/java.base/java/lang/System.class]
[正在加载/modules/java.base/java/io/PrintStream.class]
[正在加载/modules/java.base/java/lang/Appendable.class]
[正在加载/modules/java.base/java/io/Closeable.class]
[正在加载/modules/java.base/java/io/FilterOutputStream.class]
[正在加载/modules/java.base/java/io/OutputStream.class]
[正在加载/modules/java.base/java/io/Flushable.class]
[正在加载/modules/java.base/java/lang/Comparable.class]
[正在加载/modules/java.base/java/lang/CharSequence.class]
[已写入HelloWorld.class]
[共 181 毫秒]

主要分为几个部分:

  1. 语法分析部分: [语法分析...]
  2. 模块相关部分: [正在加载/modules...]
  3. 源文件搜索部分: [源文件的搜索路径: .] 。这里的 . 指的是当前目录。
  4. 类文件搜索部分1: [类文件的搜索路径: C:\Program Files\AdoptOpenJDK\jdk-11.0.8.10-hotspot\lib\modules,.]
    • 指的是为了编译这个HelloWorld需要在这些路径下搜索相应的类。搜索到后就加载。
    • 这里面的搜索路径有两个,一个是 C:\Program Files\AdoptOpenJDK\jdk-11.0.8.10-hotspot\lib\modules 还有一个是当前目录。
    • 加载所需使用的Java类库中的最基础的类,比如Object.class、注解相关字节码文件。代码中因为使用了还加载了String.class。
    • 这些类都是在 java.base 模块。该模块定义Java SE平台的基础API,是基础模块,不依赖于其他模块。
  5. 类文件搜索部分2: [正在检查HelloWorld] .
    • 加载HelloWorld这个程序中要用到的其他类。比如 System.class,PrintStream.classSystem.out 中的out是一个PrintStream类型的对象。
    • 还加载了一些代码中似乎没用到的类和接口。比如, Comparable.class 。实际上这个就是Comparable接口。我们的代码中的"HelloWorld!“就是个String对象,其实现了Comparable接口。

可以看到java不愧是一个面向对象语言,执行一个简单的HelloWrold都要载入这么多类。

3. 一个文件中包含多个类文件进行编译

如果一个.java文件中包含多个类的定义,使用javac编译该文件会产生多个.class文件。一个类生成一个.class文件。

注意:只能有一个public的class。

public class HelloWorld{
    public static void main(String[] args) {
        System.out.println("HelloWorld");
    }
}
class Test{

}

对上述代码使用

del *.class
javac -verbose HelloWorld.java
dir *.class

可以看到如下信息,代表生成了两个.class文件。

....
[已写入HelloWorld.class]
...
[已写入Test.class]

之后使用 dir *.class 也可进行验证。

4. 类路径参数:-cp或-classpath

类路径参数也是一个重要参数,格式为: -classpath <path>-cp <path>

-cp-classpath 的缩写。

该参数告诉java编译器,在编译某个文件时,去哪个路径去寻找该java代码文件所需要用到的类。因此称之为类路径。

比如 d:\test 有如下代码:

public class HelloWorld{
    public static void main(String[] args) {
        Test test = new Test();
        System.out.println("HelloWorld");
    }
}

很明显,编译该文件需要一个Test类,然而Test类并未在该文件中有定义。

如果直接执行 javac HelloWorld.java ,那么其会在 当前目录下寻找Test.class 。如果没找到,会报错。

有了 -cp 参数,我们就可以将类所需的其他.class放置到另外一个目录,编译的时候可以指定编译器的查找该路径。

了解了这个原理,我们可以自己做一个实验:

  1. d:\testjava\ 目录下新建一个Test.java,代码很简单 public class Test{}
  2. 进行编译。生成的Test.class在d:\testjava\目录下。
  3. 再执行 javac -cp d:\testjava\ HelloWorld.java ,就可以顺利完成编译。

事实上,即使你不手动编译Test.java,上面那条javac命令也会在d:\testjava\下寻找Test.java文件。如果有,就自动编译。

总结

  1. javac可以将.java文件编译成字节码文件。
  2. Java 11中的javac编译时,使用到了模块系统。所有java程序都需要 java.base 模块。
  3. 编译时会将程序所需 类与接口 全部导入。
  4. Java将类的信息都存储在.class文件中。启动Java程序时,会从.class文件中载入相关类。
  5. -d 参数可以在指定目录(directory)下生成相应的.class文件。
  6. -cp 参数可以帮java编译器寻找相应的类。这个参数很重要,应当理解。IDE中虽然不需要使用命令行,但如果用到第三方库,可能就需要对 Build Path 进行设置。这种设置实际上就是修改了相应的类路径。

动动手

在HelloWorld.java代码的基础上,分别对如下几种情况,使用 javac -verbose HelloWorld.java 来观察输出信息。并分析,这些错误都发生在哪几个阶段?

  1. 在HelloWorld.java的代码中添加错误。比如,将冒号 ; 去掉。
  2. String[] args 改成 string[] args
  3. 在main方法中添加 Person p = new Person();

参考资料