为什么Java类用空行编译不同?

时间:2022-09-10 18:27:22

I have the following Java class

我有以下Java类

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

When I compile this file and run a sha256 on the resulting class file I get

当我编译这个文件并在生成的类文件上运行sha256时,我得到了

9c8d09e27ea78319ddb85fcf4f8085aa7762b0ab36dc5ba5fd000dccb63960ff  HelloWorld.class

Next I modified the class and added a blank line like this:

接下来我修改了类并添加了一个如下所示的空白行:

public class HelloWorld {

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

Again I ran a sha256 on the output expecting to get the same result but instead I got

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

11f7ad3ad03eb9e0bb7bfa3b97bbe0f17d31194d8d92cc683cfbd7852e2d189f  HelloWorld.class

I have read on this TutorialsPoint article that:

我已阅读此TutorialsPoint文章:

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

只包含空格的行(可能带有注释)称为空行,Java完全忽略它。

So my question is, since Java ignores blank lines why is the compiled byte code different for both programs?

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

Namely the difference in the classes is that an end-of-transmission (^D) character is replaced with a end-of-text character (^C).

即,类中的差异在于传输结束(^ D)字符被替换为文本结束字符(^ C)。

5 个解决方案

#1


162  

Basically, line numbers are kept for debugging, so if you change your source code the way you did, your method starts at a different line and the compiled class reflects the difference.

基本上,行号保留用于调试,因此如果您按照自己的方式更改源代码,则方法从不同的行开始,编译的类反映了差异。

#2


68  

You can see the change by using javap -v which will output verbose information. Like other already mentioned the difference will be in line numbers:

您可以使用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:

More precisely the class file differs in the LineNumberTable section:

更准确地说,类文件在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.

LineNumberTable属性是Code属性(第4.7.3节)的attributes表中的可选variable-length属性。调试器可以使用它来确定代码数组的哪个部分对应于原始源文件中的给定行号。

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

如果Code属性的属性表中存在多个LineNumberTable属性,则它们可以按任何顺序出现。

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.

Code属性的attributes表中每行源文件可能有多个LineNumberTable属性。也就是说,LineNumberTable属性可以一起表示源文件的给定行,并且不需要与源行一对一。

#3


6  

The assumption that "Java ignores blank lines" is wrong. Here is a code snippet that behaves differently depending on the number of empty lines before the method main:

“Java忽略空行”的假设是错误的。这是一个代码片段,其行为方式不同,具体取决于方法main之前的空行数:

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

If there are no empty lines before main, it prints "foo", but with one empty line before main, it prints "bar".

如果在main之前没有空行,则打印“foo”,但在main之前有一个空行,它会打印“bar”。

Since the runtime behavior is different, the .class files must be different, regardless of any timestamps or other metadata.

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

This holds for every language that has access to the stack frames with line numbers, not only for Java.

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

#4


3  

As well as any line number details for debugging, your manifest may also store the build time and date. This will naturally be different every time you compile.

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

#5


-2  

While this may not answer your question directly, its good for knowledge sake to know that a blank line might actually impact your overall output. This is because a blank line can contain carriage returns or even hexadecimal values which cannot be seen ordinarily unless you use softwares like Notepad++.

虽然这可能无法直接回答您的问题,但知道知道空行可能会影响您的整体输出是有益的。这是因为空白行可以包含回车符或甚至十六进制值,除非您使用Notepad ++等软件,否则这些值通常无法看到。

So, if you need to encrypt, transmit or even store data, I would advise that you always do a trim on both left and right sides; better still use a regex to eliminate all hexadecimal characters which may not be easily visible to human eye.

因此,如果您需要加密,传输甚至存储数据,我建议您始终在左右两侧进行修剪;更好的是仍然使用正则表达式来消除人眼可能不易看到的所有十六进制字符。

#1


162  

Basically, line numbers are kept for debugging, so if you change your source code the way you did, your method starts at a different line and the compiled class reflects the difference.

基本上,行号保留用于调试,因此如果您按照自己的方式更改源代码,则方法从不同的行开始,编译的类反映了差异。

#2


68  

You can see the change by using javap -v which will output verbose information. Like other already mentioned the difference will be in line numbers:

您可以使用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:

More precisely the class file differs in the LineNumberTable section:

更准确地说,类文件在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.

LineNumberTable属性是Code属性(第4.7.3节)的attributes表中的可选variable-length属性。调试器可以使用它来确定代码数组的哪个部分对应于原始源文件中的给定行号。

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

如果Code属性的属性表中存在多个LineNumberTable属性,则它们可以按任何顺序出现。

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.

Code属性的attributes表中每行源文件可能有多个LineNumberTable属性。也就是说,LineNumberTable属性可以一起表示源文件的给定行,并且不需要与源行一对一。

#3


6  

The assumption that "Java ignores blank lines" is wrong. Here is a code snippet that behaves differently depending on the number of empty lines before the method main:

“Java忽略空行”的假设是错误的。这是一个代码片段,其行为方式不同,具体取决于方法main之前的空行数:

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

If there are no empty lines before main, it prints "foo", but with one empty line before main, it prints "bar".

如果在main之前没有空行,则打印“foo”,但在main之前有一个空行,它会打印“bar”。

Since the runtime behavior is different, the .class files must be different, regardless of any timestamps or other metadata.

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

This holds for every language that has access to the stack frames with line numbers, not only for Java.

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

#4


3  

As well as any line number details for debugging, your manifest may also store the build time and date. This will naturally be different every time you compile.

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

#5


-2  

While this may not answer your question directly, its good for knowledge sake to know that a blank line might actually impact your overall output. This is because a blank line can contain carriage returns or even hexadecimal values which cannot be seen ordinarily unless you use softwares like Notepad++.

虽然这可能无法直接回答您的问题,但知道知道空行可能会影响您的整体输出是有益的。这是因为空白行可以包含回车符或甚至十六进制值,除非您使用Notepad ++等软件,否则这些值通常无法看到。

So, if you need to encrypt, transmit or even store data, I would advise that you always do a trim on both left and right sides; better still use a regex to eliminate all hexadecimal characters which may not be easily visible to human eye.

因此,如果您需要加密,传输甚至存储数据,我建议您始终在左右两侧进行修剪;更好的是仍然使用正则表达式来消除人眼可能不易看到的所有十六进制字符。