what-happen-when-javac.md 5.4 KB
Newer Older
沉默王二's avatar
优化  
沉默王二 已提交
1
## Java 程序在编译期发生了什么
沉默王二's avatar
doc  
沉默王二 已提交
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

“二哥,看了上一篇 [Hello World](https://mp.weixin.qq.com/s/191I_2CVOxVuyfLVtb4jhg) 的程序后,我很好奇,它是怎么在 Run 面板里打印出‘三妹,少看手机少打游戏,好好学,美美哒’呢?”三妹咪了一口麦香可可奶茶后对我说。

“三妹,我们通常把 Java 分为编译期和运行时,弄清楚这两个阶段就知道原因了。由于运行时涉及到的内容比较多,这篇文章我们先来说说编译期,等学习了 Java 虚拟机的一些知识后再说道说道运行时。”

贴一下 HelloWorld 这段代码:

```java
/**
 * @author 微信搜「沉默王二」,回复关键字 PDF
 */
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("三妹,少看手机少打游戏,好好学,美美哒。");
    }
}
```

点击 IDEA 工具栏中的锤子按钮(Build Project,编译整个项目),如下图所示。

沉默王二's avatar
沉默王二 已提交
22
![](https://cdn.jsdelivr.net/gh/itwanger/jmx-java/images/overview/five-01.png)
沉默王二's avatar
doc  
沉默王二 已提交
23 24 25 26


这时候,就可以在 src 的同级目录 target 下找到一个名为 HelloWorld.class 的文件。

沉默王二's avatar
沉默王二 已提交
27
![](https://cdn.jsdelivr.net/gh/itwanger/jmx-java/images/overview/five-02.png)
沉默王二's avatar
doc  
沉默王二 已提交
28 29 30 31


如果找不到的话,在目录上右键选择「Reload from Disk,从磁盘上重新加载」,如下图所示:

沉默王二's avatar
沉默王二 已提交
32
![](https://cdn.jsdelivr.net/gh/itwanger/jmx-java/images/overview/five-03.png)
沉默王二's avatar
doc  
沉默王二 已提交
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96


可以双击打开它。

```java
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.itwanger.five;

public class HelloWorld {
    public HelloWorld() {
    }

    public static void main(String[] args) {
        System.out.println("三妹,少看手机少打游戏,好好学,美美哒。");
    }
}
```

IDEA 默认会用 Fernflower 反编译工具将字节码文件(后缀为 .class 的文件,也就是 Java 源代码编译后的文件)反编译为我们可以看得懂的 Java 源代码。但实际上,字节码文件并不是这样的,而是:

```
// class version 58.0 (58)
// access flags 0x21
public class com/itwanger/five/HelloWorld {

  // compiled from: HelloWorld.java

  // access flags 0x1
  public <init>()V
   L0
    LINENUMBER 6 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
    RETURN
   L1
    LOCALVARIABLE this Lcom/itwanger/five/HelloWorld; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x9
  public static main([Ljava/lang/String;)V
   L0
    LINENUMBER 8 L0
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    LDC "\u4e09\u59b9\uff0c\u5c11\u770b\u624b\u673a\u5c11\u6253\u6e38\u620f\uff0c\u597d\u597d\u5b66\uff0c\u7f8e\u7f8e\u54d2\u3002"
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   L1
    LINENUMBER 9 L1
    RETURN
   L2
    LOCALVARIABLE args [Ljava/lang/String; L0 L2 0
    MAXSTACK = 2
    MAXLOCALS = 1
}
```

是不是就有点懵逼了?新手看到这个很容易头大,不过不要担心,后面我再和大家一块深入研究一下,这里就是感受一下字节码的魅力。

那这个字节码文件是怎么看到的呢?可以通过 IDEA 菜单栏中的「View」→「Show Bytecode」查看,如下图所示。

沉默王二's avatar
沉默王二 已提交
97
![](https://cdn.jsdelivr.net/gh/itwanger/jmx-java/images/overview/five-04.png)
沉默王二's avatar
doc  
沉默王二 已提交
98 99 100

PS:字节码并不是机器码,操作系统无法直接识别,需要在操作系统上安装不同版本的 Java 虚拟机(JVM)来识别。通常情况下,我们只需要安装不同版本的 JDK(Java Development Kit,Java 开发工具包)就行了,它里面包含了 JRE(Java Runtime Environment,Java 运行时环境),而 JRE 又包含了 JVM。

沉默王二's avatar
沉默王二 已提交
101
![](https://cdn.jsdelivr.net/gh/itwanger/jmx-java/images/overview/five-05.png)
沉默王二's avatar
doc  
沉默王二 已提交
102 103 104

Windows、Linux、MacOS 等操作系统都有相应的 JDK,只要安装好了 JDK 就有了 Java 语言的运行时环境,就可以把一份字节码文件在不同的平台上运行了。可以在 [Oracle 官网](https://www.oracle.com/java/technologies/javase-jdk11-downloads.html)上下载不同版本的 JDK。

沉默王二's avatar
沉默王二 已提交
105
![](https://cdn.jsdelivr.net/gh/itwanger/jmx-java/images/overview/five-06.png)
沉默王二's avatar
doc  
沉默王二 已提交
106 107 108 109 110

PPS:为什么要查看字节码呢?查看字节码文件更容易让我们搞懂 Java 代码背后的原理,比如搞懂 Java 中的各种语法糖的本质。

相比于 IDEA 自带的「Show Bytecode」功能,我更推荐 `jclasslib` 这款插件,可以在插件市场中安装(我已经安装过了)。

沉默王二's avatar
沉默王二 已提交
111
![](https://cdn.jsdelivr.net/gh/itwanger/jmx-java/images/overview/five-07.png)
沉默王二's avatar
doc  
沉默王二 已提交
112 113 114

安装完成之后,点击 View -> Show Bytecode With jclasslib 即可通过 jclasslib 查看字节码文件了(点击之前,光标要停留在对应的类文件上),如下图所示。

沉默王二's avatar
沉默王二 已提交
115
![](https://cdn.jsdelivr.net/gh/itwanger/jmx-java/images/overview/five-08.png)
沉默王二's avatar
doc  
沉默王二 已提交
116 117 118

使用 jclasslib 不仅可以直观地查看类对应的字节码文件,还可以查看类的基本信息、常量池、接口、字段、方法等信息,如下图所示。

沉默王二's avatar
沉默王二 已提交
119
![](https://cdn.jsdelivr.net/gh/itwanger/jmx-java/images/overview/five-09.png)
沉默王二's avatar
doc  
沉默王二 已提交
120 121 122 123 124



也就是说,在编译阶段,Java 会将 Java 源代码文件编译为字节码文件。在这个阶段,编译器会进行一些检查工作,比如说,某个关键字是不是写错了,语法上是不是符合预期了,不能有很明显的错误,否则带到运行时再检查出来就会比较麻烦了。

沉默王二's avatar
沉默王二 已提交
125
![](https://cdn.jsdelivr.net/gh/itwanger/jmx-java/images/overview/five-10.png)