0
点赞
收藏
分享

微信扫一扫

只会点一下 Run 就够了?你真的见过 Java 编译器干的那些事吗?

大佬们好!我是LKJ_Coding,一枚初级马牛,正在努力在代码的丛林中找寻自己的方向。如果你也曾在调试中迷失,或是在文档中翻滚,那我们一定有许多共同话题可以聊!今天,我带着满满的代码“干货”来和大家分享,学不学无所谓,反正我先吐槽了!

前言

  “写完 Java 代码,点一下 Run 就 OK 了嘛!”   哥,你有没有认真想过——你那一行行 Java 代码,是怎么一步步变成字节码、怎么走过 javac 的流程、编译器在背后为你擦了多少屁股?

  别以为编译器只是“翻译器”,它可是整个 Java 工程体系的底座。不会玩 javac,你写再多 Lambda、再多注解,其实都没用对地方。

一、Java 编译流程:源码到字节码的七十二变

Java 编译器(javac)干了啥?不就是把 .java 文件变成 .class 吗?

这么想你就错了,编译过程其实是复杂多阶段的 pipeline

Java 源码的编译主要分为几步:

  1. 词法分析(Lexical Analysis)

    • 把源码字符串拆成 Token,比如标识符、关键字、分号等
  2. 语法分析(Syntax Analysis)

    • 检查代码结构是否符合 Java 语法规则,构建 AST(抽象语法树)
  3. 语义分析(Semantic Analysis)

    • 变量类型检查、方法重载匹配、泛型参数推断、名字解析
  4. 注解处理(Annotation Processing)

    • 运行编译期注解处理器(APT),比如 @AutoService、@Entity、Lombok、Dagger2
  5. 生成字节码(Bytecode Generation)

    • 把 AST 转成 JVM 指令,写成 .class 文件
  6. 后处理(可选,plugins)

    • 一些 build 工具会加字节码增强、代码检查等

你写的每一个注解、每一个泛型、每一个 lambda,都会在这个流程中被一步步“消化”掉。

图示理解:

.java 源码
  |
  |--- 词法/语法分析
  |
  |--- 语义分析
  |
  |--- 注解处理(APT)
  |
  |--- 字节码生成
  |
.class 字节码

二、javac 命令详解:常用参数你会几个?

平时谁还手动用 javac?——你要是真的懂工程、懂性能、懂注解、懂源码分析,javac 必须用得 6。

最常用的 javac 参数清单

参数 作用 常用场景
-d 指定输出目录 -d out/
-classpath-cp 指定依赖 jar/class 路径 -cp lib/*
-sourcepath 指定源码路径 多模块工程
-encoding 指定源码文件编码 -encoding UTF-8
-source 指定源码版本 -source 11
-target 指定生成字节码版本 -target 8
-parameters 保留方法参数名 反射/框架必备
-processor 指定注解处理器 APT/代码生成
-proc:none 禁用注解处理 某些特殊场景
-Xlint:all 启用所有警告 代码检查
-Werror 警告当错误 严格代码规范

典型编译命令实例

最基础

javac Hello.java

指定输出目录+依赖

javac -d out -cp lib/* src/com/myapp/*.java

编译多版本兼容代码

javac -source 1.8 -target 1.8 -encoding UTF-8 MyApp.java

开启参数名保留(Spring/反射框架开发必开)

javac -parameters -d out MyController.java

三、编译期注解处理:你以为 @Override 只是提示用的?

你用过 @Override@Deprecated@SuppressWarnings,但你未必知道**真正的大杀器其实是编译期注解处理器(Annotation Processor)**!

1. 什么是编译期注解处理器(APT)?

  • 可以在编译时扫描源代码中的注解,自动生成代码、校验逻辑、资源文件等
  • 典型场景:Lombok 自动生成 getter/setter,Dagger 自动生成依赖注入代码,AutoValue/AutoService 代码生成

2. 如何写一个自定义注解处理器?

第一步:定义注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface MyAnnotation {}

第二步:实现 AbstractProcessor

@SupportedAnnotationTypes("com.example.MyAnnotation")
@SupportedSourceVersion(SourceVersion.RELEASE_11)
public class MyProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
        for (Element elem : env.getElementsAnnotatedWith(MyAnnotation.class)) {
            // 生成代码、检查逻辑等
        }
        return true;
    }
}

第三步:注册处理器

META-INF/services/javax.annotation.processing.Processor 文件中填写:

com.example.MyProcessor

3. 用 javac 手动指定注解处理器

javac -processor com.example.MyProcessor -cp my-processor.jar -d out MyClass.java

你还可以用 -Akey=value 给注解处理器传递自定义参数。

4. APT 典型应用场景

  • Lombok:无感 getter/setter/toString
  • MapStruct:自动生成对象映射代码
  • Dagger/Hilt:依赖注入的代码生成
  • MyBatis Generator:Mapper 自动生成

四、javac 调优与源码分析 Tips

误区 / 坑点 建议
用 IDE 就不用管 javac? 复杂项目/自动化构建/jar 插件开发时手动 javac 更灵活
-target 高于 -source? 不建议,保证 source/target 版本一致
依赖路径写错 必须绝对/相对路径,jar 用 * 通配
注解处理慢 适当用 -proc:none 禁用不必要的处理器
参数名丢失 Spring/反射要加 -parameters

五、结语:Javac 不是“点一下”,而是工程世界的幕后英雄

  每个 Java 工程师都应该有一天静下心来——打开命令行,手动写一遍 javac 编译命令,看看自己写的注解如何被扫描、自己写的 Processor 如何生成代码。

  你会发现:编译器才是你代码“能跑起来”的真正引擎,而 javac 的每一个参数、每一次注解扫描、每一次字节码生成,都直接影响着你项目的生产力与扩展力。

  会写代码是入门,会用好编译器才是真正迈进工程的大门

好啦,废话不多说,今天的分享就到这里!如果你觉得我这“初级马牛”还挺有趣,那就请给我点个赞、留个言、再三连击三连哦!让我们一起“成精”吧!下次见,不见不散!

举报

相关推荐

0 条评论