为什么给Java代码加个空行,class文件就翻脸不认人了?

开发 后端
给Java源文件加个空行之后,它生成的字节码,会有变化么?

 [[315266]]  

  1. public class HelloWorld {     
  2.     public static void main(String[] args) { 
  3.         System.out.println("love xjjdog"); 
  4.      } 

为了写出这几行优美的代码,主要是为了让它输出优美动听的乐符,我下了一番功夫。你不要觉得简单,我把它打印出来给普通的保洁阿姨去看,阿姨竟然连xjjdog都认不出来。别说代码了,中英文混血,就秒杀一大堆高干分子。

想说爱我就那么难么?怎么这么多的废话呢?这次探讨的主要问题是,给Java源文件加个空行之后,它生成的字节码,会有变化么?

1、翻脸不认人

Java号称一次编译到处运行,大概就是class文件的功劳。不同的Java版本编译之后的class文件那是肯定不一样的,因为里面有一个版本号,那肯定影响了它们的内容。

我们就看一下,如果给上面的代码,加一个空行,它的class文件会不会变。

这个空行还不能随便加。它可能在xjjdog上面,也可能在下面。可能在{中,也可能在文件末尾。

1.1、打脸

在验证之前,我们先看一下当前的class文件md5值。 

 

我非常喜欢被打脸,所以先看一种加空行也无所谓的情况。 

 

再次编译之后看md5值,果然被打脸了。还好我已经练就了脸不红心不跳的本领,这个结果厚着脸皮接受。 

 

1.2、抹药

为了和主题遥相呼应,安慰一下受伤的心灵,我们把空行转移到了这里。 

 

再次编译之后,看md5值(怎么感觉这句话已经说过了呢)。

变了。这次真的变了。 

 

使用hexdump命令分析两次生成的字节码,发现其中只不过变了一个数字。 

 

2、骚戴斯乃

特别不喜欢分析这种二进制的东西。虽然CAFEBABE这个魔数在第一行历历在目。咖啡宝贝?怎么听着像是某个番号?

我们还是用javap来看一下它的原型。

javap -p -v HelloWorld.class

通过对比两次生成的字节码,我们终于发现了这个变动,是一个叫做LineNumberTable的结构引起的。 

 

使用asmtools.jar深入分析这个结构,可以看到同样的信息。 

 

LineNumberTable展示了Java源码行号和字节码指令的对应关系。前面的数字代表Java源代码中的行号,而冒号后面的则代表字节码里每行指令的映射关系。在对代码进行调试的时候,能够快速定位,顺利进行。

也就是说,这些是辅助信息,我们可以在编译的时候抹掉它。怎么抹掉呢?给javac一个参数就ok了。

javac -g:none HelloWorld.java

这样编译后的字节码,紧凑、优雅、无用。不管你加多少空行,生成的字节码都是一样的。可是,我们再也不能畅快淋漓的进行调试了。

  1.   public HelloWorld(); 
  2.     descriptor: ()V 
  3.     flags: ACC_PUBLIC 
  4.     Code: 
  5.       stack=1, locals=1, args_size=1 
  6.          0: aload_0 
  7.          1: invokespecial #1                  // Method java/lang/Object."<init>":()V 
  8.          4: return 
  9.  
  10.   public static void main(java.lang.String[]); 
  11.     descriptor: ([Ljava/lang/String;)V 
  12.     flags: ACC_PUBLIC, ACC_STATIC 
  13.     Code: 
  14.       stack=2, locals=1, args_size=1 
  15.          0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream; 
  16.          3: ldc           #3                  // String Hello xjjdog 
  17.          5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V 
  18.          8: return 

要想在开发阶段让字节码又香又有用,可以直接使用参数-g开启所有调试信息。IDEA可以在编译选项里对这个参数进行开启。有很多同学在编译之后的代码里找不到局部变量的符号表,也是由于这个参数没有开启所引起的。

 

 

责任编辑:武晓燕 来源: xjjdog
相关推荐

2020-02-19 11:16:40

Javaclass代码

2024-02-05 22:48:32

系统代码

2021-09-14 08:50:38

Kubernetes容器Docker

2019-04-09 10:45:18

IPv6运营商协议

2021-09-30 06:31:12

Spring Boot配置密码

2024-03-29 08:10:43

索引失效SQL

2017-02-27 15:19:04

2018-04-02 10:58:28

大数据sqoop大数据项目

2018-04-11 09:50:04

大数据

2020-04-17 19:41:57

基础架构即代码平台即代码云计算

2021-03-01 08:19:09

Java 包装类对象

2021-08-26 11:10:42

架构运维技术

2024-11-19 18:03:04

2021-08-10 23:09:55

区块链数据技术

2018-03-12 09:52:22

Linux命令rm

2019-09-06 10:31:45

软件开发地图

2020-06-10 09:06:48

MongoDB架构高可用

2024-06-24 07:58:00

2023-07-23 17:19:34

人工智能系统

2023-03-20 07:23:45

Docker开源存储库
点赞
收藏

51CTO技术栈公众号