老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虚拟机的历险记,无比羡慕,恨不得自己也去虚拟机走一遭,可惜身份所限,无法成行。

(码农翻身注: 《我是一个Java Class》中讲述了虚拟机历险记)

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

“不怎么样,有种大卸八块的感觉,新生成的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呆在这里。

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

  1. html文本框 控件,标签和文本框都用于显示文本

    用标签和文本框都可以显示文本信息,二者有什么区别 标签和文本框控件都可以显示文本,它们之间有什么区别?标签只能显示一横... 文本框可以显示很多横... 不对.标签框(Label)不能输入文本. VB ...

  2. 川大计算机学院的牛人走的都是同一条道路

    今天听到liulike师兄也去了MSRA,当然,以他的实力,的确应该去MSRA才对.liulike师兄大我一届,是川大ACM队的牛人,今年代表川大ACM队在全国取得了历史最好成绩. 又是一个去MSRA ...

  3. 是不是每个人的大学都很迷茫?

    不知道每个人如何,但我的大学是不迷茫的:单纯.快乐.无忧无虑--奔着理想往前冲.当然,后面的事你应该都知道了:冲到坑里去了.前面不迷茫,后面就要迷茫.迷茫是好事,说明你开始思考:但迷茫也是个麻烦事,如 ...

  4. 奔四的技术人,内心都有哪些波澜?

    曾几何时,作为二十多岁的技术人,我也常常关注关于三十多岁技术人的话题,而恍惚间,我不再关注这个话题了,而是关注关于四十多岁技术人的话题,这是因为自己,正在奔四的路上了嘛.知乎上蛮多关于四十岁后失业的话 ...

  5. 人魔比妖都恶的时代...

    人魔比妖都恶的时代... 转载于:https://www.cnblogs.com/socrassi/archive/2009/09/30/1576859.html

  6. 一个真正容易成大事的人,大多都熬过这4种苦,看看你熬过几种?

    大器,最早是指"天"的意思:孔颖达评论大器:"大器,谓天也".随着时代演变,又指有大才.能做大事的人:成就大器之路,必然是坎坷且艰辛的.<孟子·告下> ...

  7. 一切想要发财的人,你都要善于看到隐形的东西

    一切想要发财的人,你都要善于看到隐形的东西,创业也是这个样子的,不要待在穷地方创业.找一个富有的地方发展,这样机会更多. 比如我现在创业,工作很忙没时间做到,就叫一个外卖,然后你有更多的时候去工作.你 ...

  8. 转:优秀的人,往往都具备这5种视角

    个人理解: 上帝视解:深入其中,能敏锐感受内里变化:置身其外,又能让自己变成一个旁观者,观察很多事情的发生和结果. 自己出发的视角,我们看到的世界就会过于局限:引入多元视角,就会看到完全不同的情境,产 ...

  9. DayDayUp:2019.01.24新东方年会—俞敏洪点赞并奖励吐槽神曲《释放自我》12万元!—附全歌词(牛逼的人在哪里都不会彷徨)

    DayDayUp:2019.01.24新东方年会-俞敏洪点赞并奖励员工的吐槽神曲<释放自我>12万元!-附全歌词 目录 新东方年会神曲亮了 官微回应:放手去做,未来可期! 各家吃瓜 < ...

最新文章

  1. pywinauto 记事本操作示例 移动鼠标 使用键盘 自动操作软件
  2. 零基础可以学python吗-零基础适合学习python吗?
  3. windows terminal 笔记
  4. python中使用if __name__ == '__main__':
  5. centos7: ifconfig出现command not found解决办法
  6. 随时发生的网络攻击怎么防?这是一场网络安全的全民保卫战!
  7. mysql 因单个表过大导致导入数据库失败
  8. 人脸识别之特征脸方法(Eigenface)PCA方法
  9. 贴片电容封装及尺寸示意图
  10. mysql key语句_mysql建表语句key的含义
  11. 黑莓手机刷Linux系统,黑莓Priv系统刷机包下载及一键刷机方法步骤教程
  12. wordpress炫酷主题Salient最新版13.0.5 汉化版免费下载
  13. 微信网页版(在电脑上聊微信)
  14. 智齿客服网页端接入文档V2.3
  15. SAM4E单片机之旅——21、DMAC之USART回显
  16. 关于java的索引问题
  17. 【P05】小巧简单的 OP+ClassAB 低压供电耳放
  18. 密集恐惧症候群测试图
  19. 2021华数杯C题总结
  20. 人总要有奋斗目标的坚持

热门文章

  1. [译文]Domain Driven Design Reference(五)—— 为战略设计的上下文映
  2. python练习集100题(21-40)
  3. 《嵌入式系统开发之道——菜鸟成长日志与项目经理的私房菜》——第1章 系统•嵌入•硬件 01-01 Welcome on board!...
  4. http://blog.csdn.net/java2000_wl/article/details/8627874
  5. Oracle 块修改跟踪 (Block Change Tracking) 说明
  6. ubuntu下修改文件夹权限
  7. WCF 第十二章 对等网
  8. Python之max(num, key=lambda x:x[0])用法的详细解析
  9. 给“小白”图示讲解OFDM(正交频分复用)的原理
  10. Linux中 set、env、declare、export显示shell变量的区别