Java编译器javac

Java编译器javac

IDE 帮我们做了太多事情,编译、打包、执行等等操作,在 IDE 上点一下就能完成,那如果没有了 IDE 呢,你还会这些基本操作么?本文 通过介绍 javac (Java compiler) ,让你脱离 IDE 也能编译项目。


一、简介

The javac tool reads class and interface definitions, written in the Java programming language, and compiles them into bytecode class files.

—— from [man javac]

javac 全称 Java compiler,简单的说负责将 java 源码文件编译为 class 字节码文件。

《深入理解 Java 虚拟机》中也将 javac 编译器叫做前端编译器,另外还有后端运行期编译器( JIT 编译器, Just In Time Compiler)和静态提前编译器( AOT 编译器,Ahead Of Time Compiler)

在安装 JDK 环境时,一般是跟其他 java 工具命令一样在 JAVA_HOME/bin 目录下。

二、基础语法

javac 的基本语法是

javac [ options ] [ sourcefiles ] [ @argfiles ]

options 命令行参数选项,

sourcefiles 待编译的源文件,一个或者多个

@argfiles 存放待编译源文件列表的文件

sourcefiles 和 argfiles 没什么深入的。 options 参数比较多,会在下文对重要参数进行分析。

三、类型查找

编译时,编译器会需要一些没有出现在命令行源文件列表中的类型信息。

编译源文件时,编译器通常需要一个类型的信息,该类型的定义没有出现在命令行上给出的源文件中。编译器需要源文件中使用、扩展或实现的每个类或接口的类型信息。这包括源文件中未明确提及但通过继承提供信息的类和接口。

四、使用说明

1、基本编译

javac
   |- classes
   |- src
   |   |- com
   |       |- wdado
   |            |- javac
   |                 |- ForJavac.java
package com.wdado.javac;

public class ForJavac {
    public static void main(String[] args) {
        System.out.println("Hello javac");
    }
}

在 src 目录下执行 javac,

➜ src javac com/wdado/javac/ForJavac.java

编译出的字节码 ForJavac.class 默认与源文件在同一目录

javac
   |- classes
   |- src
   |   |- com
   |       |- wdado
   |            |- javac
   |                 |- ForJavac.java
   |                 |- ForJavac.class

2、Options

具体详细的Options列表和使用说明,请参考Oracle官网JavaSE8 javac文档 或者 man javac。这里对一些个比较重要的参数进行说明和简单示例。

(1)-d 和 -verbose

-d 比较好理解,指定编译出的 class文件存放位置,而不是默认与源码放在一个位置,方便组织class文件。

这里再提一个选项 -verbose ,该选项可以输出编译器正在执行的操作消息,后面会通过这个选项查看一些有用的信息。

(2)-cp (-classpath) 和 -sourcepath

-classpath 和-sourcepath 个人理解,简单的说都是作用于查找用户类(被使用、依赖的类)。查找用户类时,用户类可以是源码文件,也可以是 class 文件,但是最终编译器需要的肯定是 class 文件。

注意点:

  • 两个参数都不指定时,sourcepath 默认是 javac 执行路径,也就是 ./ 。
  • 单独指定 classpath 时,类型查找会在 classpath 中查找源文件类文件
  • 单独指定 sourcepath,会在 CLASSPATH(没有的话则当前目录./)搜索用户类文件,在sourcepath指定源文件(.java)。注意这个时候有sourcepath时,classpath 不会再查找源文件了,而是只找 class文件。

接下来,以下面目录结构和代码为基础,对这两个参数使用上做 一些说明和示例。

javacdemo
   |- classes
   |- src
   |    |- com
   |         |- wdado
   |              |-javac
   |                  |- ForJavac.java
   |         |- PrintHelper.java
package com.wdado.javac;
​
import com.wdado.PrintHelper;
​
public class ForJavac {
    public static void main(String[] args) {
        System.out.println("hello javac");
        new PrintHelper().print("hello javac from helper");
    }
}
package com.wdado;
​
public class PrintHelper {
    public void print(String s) {
        System.out.println(s);
    }
}

3、简单示例

(1)现在我们要在 src 目录执行javac 编译 ForJavac.java ,输出到classes文件夹

当前 src 目录就是源码目录,所以采用默认情况就可以,最简单的方式:

➜ src javac com/wdado/javac/ForJavac.java -verbose -d ../classes

(2)如果要在 src 的上一层 javacdemo目录执行 javac 呢?

这个时候不指定 classpath 和 sourcepath 是无法编译通过的,会提示找不到 PrintHelper。因为此时源文件和类文件搜索路径都是 ./,也就是 javacdemo/,而实际上应当是 src

➜  javacdemo  javac src/com/wdado/javac/ForJavac.java -verbose -d classes
[解析开始时间 RegularFileObject[src/com/wdado/javac/ForJavac.java]]
[解析已完成, 用时 20 毫秒]
[源文件的搜索路径: .]
[类文件的搜索路径: /Library/Java/JavaVirtualMachines/jdk1.8.0_74.jdk/Contents/Home/jre/lib/resources.jar,.....,/System/Library/Java/Extensions/MRJToolkit.jar,.]
src/com/wdado/javac/ForJavac.java:3: 错误: 找不到符号
import com.wdado.PrintHelper;
                ^
  符号:   类 PrintHelper
  位置: 程序包 com.wdado
[正在加载ZipFileIndexFileObject[/Library/Java/JavaVirtualMachines/jdk1.8.0_74.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Object.class)]]
...
[正在加载....
src/com/wdado/javac/ForJavac.java:8: 错误: 找不到符号
        new PrintHelper().print("hello javac from helper");
            ^
  符号:   类 PrintHelper
  位置: 类 ForJavac
[共 229 毫秒]
2 个错误

所以需要我们指定 classpath (单独指定classpath 会在 src 搜索源文件和 class 文件)

➜ javacdemo javac -classpath src  src/com/wdado/javac/ForJavac.java -verbose -d classes

或者 sourcepath(因为 ForJavac.java 的额外依赖 PrintHelper 也是 src 下)

➜ javacdemo javac -sourcepath src  src/com/wdado/javac/ForJavac.java -verbose -d classes

(3)如果现在目录结构和代码是这样的:

javacdemo
   |- classes
   |- src
   |   |- com
   |       |- wdado
   |           |- javac
   |               |- ForJavac.java
   |           |- PrintHelper.java
   |- src2
   |   |- com
   |       |- wdado2
   |           |- PrintHelper2.java
package com.wdado.javac;

import com.wdado.PrintHelper;
import com.wdado2.PrintHelper2;

public class ForJavac {
    public static void main(String[] args) {
        System.out.println("hello javac");
        new PrintHelper().print("hello javac from helper");
        new com.wdado2.PrintHelper2().print("hello javac from helper");
    }
}
package com.wdado2;

public class PrintHelper2 {
    public void print(String s) {
        System.out.println(s);
    }
}

下面的的方式能编译成功么?

➜ javacdemo javac -classpath src2  -sourcepath src src/com/wdado/javac/ForJavac.java -verbose -d classes

答案是不行,因为找不到 PrintHelper2。

注意:如果指定了soucepath,classpath路径不会再查找源文件了,而是只找 class文件

正确方式应该是

➜ javacdemo javac -sourcepath src:src2 src/com/wdado/javac/ForJavac.java -verbose -d classes

编译完成后的目录结构

javacdemo
   |- classes
   |-  |- com
   |       |- wdado
   |           |- javac
   |                 |-ForJavac.class
   |           |-- PrintHelper.class   
   |       |- wdado2
   |           |- PrintHelper2.class
   |- src
   |   |- com
   |       |- wdado
   |           |- javac
   |               |- ForJavac.java
   |               |- PrintHelper.java
   |- src2
   |   |- com
   |       |- wdado2
   |           |- PrintHelper2.java

假如我们这个时候删除 src2目录,需要重新编译 ForJavac.java呢?注意当前目录并非是在源码目录

➜ javacdemo javac -classpath classes/ -sourcepath src src/com/wdado/javac/ForJavac.java -verbose -d classes

如果在源码目录 src,则需要如下命令,同时指定两个参数

五、javac 脑图

javac脑图
javac脑图

六、其他

1、个人环境信息

本文中 Java 版本信息

java version “1.8.0_74”
Java(TM) SE Runtime Environment (build 1.8.0_74-b02)
Java HotSpot(TM) 64-Bit Server VM (build 25.74-b02, mixed mode)

本文 OS 信息

macOS High Sierra 10.13.3

2、参考文档

Oracle Javase 8 Javac文档

https://docs.oracle.com/javase/8/docs/technotes/tools/unix/javac.html

https://deors.wordpress.com/2011/10/31/annotation-generators/

https://blog.csdn.net/zq_zhang/article/details/53924779

发表评论

电子邮件地址不会被公开。 必填项已用*标注