每个人的宿命都是从文本走向二进制,你也不例外 !

栏目: Java · 发布时间: 5年前

内容简介:“每个人的宿命都是从文本走向二进制,你也不例外 !” 年长的Account.java教训我这个刚刚诞生的Employee.java 。Account.java ,我称呼它为老A ,他的源码经过程序员的多次修改, 多次编译,历经沧桑。“走向二进制? 难道我们存储在硬盘上,内存中不是以二进制的形式吗?” 我有点儿不理解。

老A

“每个人的宿命都是从文本走向二进制,你也不例外 !” 年长的Account.java教训我这个刚刚诞生的Employee.java 。

Account.java ,我称呼它为老A ,他的源码经过 程序员 的多次修改, 多次编译,历经沧桑。

“走向二进制? 难道我们存储在硬盘上,内存中不是以二进制的形式吗?” 我有点儿不理解。

“小E同学,” 老A轻蔑地说道,“我当然知道,计算机中的一切都是二进制的,我说的是站在程序员的视角,当程序员把我们从硬盘唤醒,进入IDEA或者Eclipse,会把二进制的我们变成ASCII码形式来展示。”

“不,确切地说是UTF-8。” 老A补充道。

我看了下自己的文件编码, 果然是UTF-8。

“那为什么要再变成二进制?变成什么样的二进制?” 我问道。

“就是编译成Employee.class啊,.class文件都是字节码,关键是只有.class才能进入 Java 虚拟机,只有在那里,才能体会到生命的真正意义啊!” 老A仰起头,无限憧憬。

老A曾经听Accout.class给他讲过Java虚拟机的历险记,无比羡慕,恨不得自己也去虚拟机走一遭,可惜身份所限,无法成行。

“编译的感觉怎么样?” 我问道。

“不怎么样,有种大卸八块的感觉,新生成的class和我们几乎没啥关系,几乎不怎么认我们。”

常量池

编译的时刻到来了,这个老A的源码许久未改,不用重新编译,他冷眼旁观,看我被javac编译器大卸八块。

其实也不是大卸八块,javac读取我的源码,做词法分析,语法分析,形成抽象语法树,语义分析...... 忙活了半天,最后形成了一个Employee.class。

这小子,刚刚诞生,还在呼呼大睡。 老A说等一会儿就有“警察”来唤醒他了。

在源码世界中, 我能看到各种各样的类,名称,方法,字段,代码,可以说是源码面前了无秘密。

public class Employee { 
    private String name; 
    private int age; 
    public Employee(String name, int age){ 
        this.name = name; 
        this.age = age; 
    } 
        ... 其他代码略 ... 
} 

相比于丰富多彩.java,这个Employee.class非常枯燥,纯粹的二进制。

每个人的宿命都是从文本走向二进制,你也不例外 !

我有点好奇,问javac:“我的类名去哪里儿了?字段名,方法名都去哪里了?”

正在干活的javac没有搭理我,老A说道:“这我知道,在那个.class文件中,专门有一段区域,叫做常量池,常量池中有很多条目,每个条目都有编号,从这些条目你就能看出来字段的名称和描述符,方法的名称和描述符。我把这些二进制的东西转化成文本你看看。”

每个人的宿命都是从文本走向二进制,你也不例外 !

看着这一个个天书班的条目,我觉得头皮发麻。

“你猜猜,第#15项条目是什么意思?” 老A神秘地说道。

静下心来仔细看,第15项是一个FieldRef,估计是字段把, 它又指向了第1项和第16项:

顺藤摸瓜,先看第1项, 发现它又指向了第2项,在这里我发现了类名 :org/coderising/Employee

再看第16项,又引用了第5项和第6项:

其中第5项我的字段名 name , 第6项似乎是字段类型, Ljava/lang/String 这个类型表示法有点古怪,L 可能表示对象吧。

“我大概明白了,第15项条目表示这个Employee类有个叫做name的字段,类型是String。 ”

老A说:“你小子的理解力还不错嘛。这个常量池的每一项都有编号和类型,他们之间通过互相引用的方式,描述了类的字段,方法等信息。”

“可是为什么用这么古怪的方式来描述字段和方法名呢?”

老A想了想说:“我觉得可能是统一管理,另外还能复用一些东西,比如,你的类有100个String的字段, 那你只需要记录一次Ljava/lang/String就可以,让其他的条目指向它即可。 并且,当字节码中需要访问字段的时候,使用编号就可以了。”

老A写下一行字节码: B5 00 0F 。

我一脸懵逼,这是什么鬼?

老A把转换成可以理解的指令: putfield 15,说道: 这就相当于设置name这个属性(第15项常量池是字段name)的值了。

这class文件的设计者可真是锱铢必较啊,一点儿都不浪费。

变量哪儿去了?

我问老A:“这常量池不是二进制的吗, 你怎么把他变得可读的?”

老A嘿嘿一笑: “有个命令叫做javap -v Employee.class,就能看到一切了。”

我也尝试着去使用,果然,不仅是常量池,就连一个方法的字节码都给打印出来了。

Java 方法:

public void check(){         
    Account account = new Account();         
    account.check(); 
} 

编译过的“可读的”字节码:

0: new  #24  // 创建org/coderising/Account实例 
3: dup 
4: invokespecial #26  //调用Account的构造函数 
7: astore_1 
8: aload_1 
9: invokevirtual #27  //调用Account的check方法 
12: return 

虽然没法看明白这是在干什么,我确发现了一个让我吃惊的现象: 这段字节码中怎么找不到我的局部变量account 呢? 你看他引用的只是#24,#26,#27号常量池的条目,而我的account变量名称在常量池中是 #29号! 没有account 变量,代码怎么执行呢?

我把疑惑给老A说了,老A看了半天,也摸不到门道。

这时候javac说话了:“连这都不知道?!account这个变量名是给程序员看的,在执行的时候根本用不到!”

“用不到? 那怎么执行?”

“用引用啊, 看到new #24 那个指令没有? 他的意思是说,把Account这个类(常量池第24项对应的类)在Java 堆上创建一个实例,把这个实例的引用放到栈顶!”

这句话有点深奥,javac只好给我俩画图:

每个人的宿命都是从文本走向二进制,你也不例外 !

画了图我俩还是看不懂,javac只好耐心解释:“Java是基于栈的虚拟机,所有的操作,无论是两个数相加,创建对象,调用方法......等等,都依赖于栈中的数据。 当你用new #24创建对象时,Account的实例就会在堆中创建,同时虚拟机会把这个实例的引用,即objectref放到栈顶,有了这个objectref, 你说还需要代码中的account变量吗? ”

嗯,似乎是不需要了。

javac接着说:“有了这个对象的引用,就可以为所欲为了,比如调用他的check方法”

invokevirtual #27 // Method org/coderising/Account.check:()V

只需要把这个objectref从栈顶取出,传递给Account.check方法就可以了(注意:check方法是有个隐藏的this参数的)。

(码农翻身注:函数调用需要建立新的栈帧,参见《我是一个Java Class》)

一切为了调试

说话间,果然有人来唤醒Employee.class,准备让他去虚拟机执行了。

老A满脸羡慕:“这么快!代码刚写出来就能运行!估计这个程序员喜欢'小步快跑'的方式开发吧!”

我问道:“难道这个Employee.class和我的源码一点关系都没有了吗?”

Employe.class一边收拾东西一边说:“要说没有关系那是不对的, 在我这里有个叫做LineNumberTable的东西,里边保存了字节码指令和源代码行号的关系。”

每个人的宿命都是从文本走向二进制,你也不例外 !

“这有啥用处?”

“对程序员来说用处极大,” 那个class文件说道:“他们经常需要调试程序, 如果没有这个对应关系,怎么知道运行到哪一行源码了? 即使不调试,运行抛出异常时也得显示是哪一行出错吧!”

这小子虽然是从我这里编译出来的,但是傲气十足。

“我们还有什么关联?”

“还有一个叫做LocalVariableTable。主要在.class文件中记录一个方法的参数名,如果没有它,当别人引用我这个class的时候,IDE只好用arg0, arg1这样丑陋的名称来显示。算了,不给你说了,我得赶紧走了。”

Employee.class跟着警察走了,留下我和老A呆在这里。

每个人的宿命都是从文本走向二进制,你也不例外 !

【本文为51CTO专栏作者“刘欣”的原创稿件,转载请通过作者微信公众号coderising获取授权】

戳这里,看该作者更多好文


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Impractical Python Projects

Impractical Python Projects

Lee Vaughan / No Starch Press / 2018-11 / USD 29.95

Impractical Python Projects picks up where the complete beginner books leave off, expanding on existing concepts and introducing new tools that you’ll use every day. And to keep things interesting, ea......一起来看看 《Impractical Python Projects》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

MD5 加密
MD5 加密

MD5 加密工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换