为什么 Java class 编译时有一个空行?

Why does a Java class compile differently with a blank line?

我有以下Javaclass

public class HelloWorld {
  public static void main(String []args) {
  }
}

当我编译此文件并在生成的 class 文件上 运行 sha256 时,我得到

9c8d09e27ea78319ddb85fcf4f8085aa7762b0ab36dc5ba5fd000dccb63960ff  HelloWorld.class

接下来我修改了 class 并添加了一个空行,如下所示:

public class HelloWorld {

  public static void main(String []args) {
  }
}

我再次 运行 输出上的 sha256 期望得到相同的结果,但我得到的是

11f7ad3ad03eb9e0bb7bfa3b97bbe0f17d31194d8d92cc683cfbd7852e2d189f  HelloWorld.class

我在 this TutorialsPoint article 上读到:

A line containing only white space, possibly with a comment, is known as a blank line, and Java totally ignores it.

所以我的问题是,由于 Java 忽略空行,为什么两个程序的编译字节码不同?

即区别在于 HelloWorld.class 中的 0x03 字节被 0x04 字节替换。

基本上,行号是为了调试而保留的,所以如果您按照原来的方式更改源代码,您的方法将从不同的行开始,编译后的 class 会反映出差异。

您可以使用 javap -v 查看更改,这将输出详细信息。与其他已经提到的一样,区别在于行号:

$ javap -v HelloWorld.class > with-line.txt
$ javap -v HelloWorld.class > no-line.txt
$ diff -C 1 no-line.txt with-line.txt
*** no-line.txt 2018-10-03 11:43:32.719400000 +0100
--- with-line.txt       2018-10-03 11:43:04.378500000 +0100
***************
*** 2,4 ****
    Last modified 03-Oct-2018; size 373 bytes
!   MD5 checksum 058baea07fb787bdd81c3fb3f9c586bc
    Compiled from "HelloWorld.java"
--- 2,4 ----
    Last modified 03-Oct-2018; size 373 bytes
!   MD5 checksum 435dbce605c21f84dda48de1a76e961f
    Compiled from "HelloWorld.java"
***************
*** 50,52 ****
        LineNumberTable:
!         line 3: 0
        LocalVariableTable:
--- 50,52 ----
        LineNumberTable:
!         line 4: 0
        LocalVariableTable:

更准确地说,class 文件在 LineNumberTable 部分有所不同:

The LineNumberTable attribute is an optional variable-length attribute in the attributes table of a Code attribute (§4.7.3). It may be used by debuggers to determine which part of the code array corresponds to a given line number in the original source file.

If multiple LineNumberTable attributes are present in the attributes table of a Code attribute, then they may appear in any order.

There may be more than one LineNumberTable attribute per line of a source file in the attributes table of a Code attribute. That is, LineNumberTable attributes may together represent a given line of a source file, and need not be one-to-one with source lines.

除了用于调试的任何行号详细信息外,您的清单还可能存储构建时间和日期。这个自然每次编译都不一样

"Java ignores blank lines"的假设是错误的。这是一个代码片段,根据方法 main:

之前的空行数,其行为会有所不同
class NewlineDependent {

  public static void main(String[] args) {
    int i = Thread.currentThread().getStackTrace()[1].getLineNumber();
    System.out.println((new String[]{"foo", "bar"})[((i % 2) + 2) % 2]);
  }
}

如果main之前没有空行,则打印"foo",但main之前有一个空行,则打印"bar".

由于运行时行为不同,.class 文件 必须 不同,无论任何时间戳或其他元数据如何。

这适用于所有可以访问带行号的堆栈帧的语言,不仅适用于 Java。

注意:如果使用-g:none编译(没有任何调试信息),则不会包含行号,getLineNumber()总是returns -1,并且该程序总是打印 "bar",而不管换行符的数量。