写了那么多 Java 代码,却不一定见过它的真面目
我们都知道那句著名的宣传语——「一次编写, 到处运行(Write Once, Run Anywhere)」,这是对 Java 平台无关性的精准概括。字节码 + JVM 使其平台无关,与此同时也衍生出另一个无关性——语言无关性。像 Kotlin、Groovy、Scala、Clojure、JRuby 等语言都可以运行在 JVM 之上。理论上只要能将源代码编译成符合 JVM 规范的字节码,任何语言都可以在 JVM 中运行。
字节码是连接语言和 JVM 的关键桥梁,今天我们就一起聊聊这个关键人物。
概览
我们创建一个 User 类:
package com.shuijing;
public class User {
}
然后通过 javac 命令编译,得到 User.class 文件。然后用支持十六进制的文本工具(比如 sublime)打开,你会看到如下内容:
Class 十六进制
在一堆看似毫无规律的字符中,我们发现开头几个字好像有什么特别的含义——cafe babe,咖啡宝贝?没错,这也解释了为啥 Java 的商标是一杯冒着热气的咖啡。开头这四个字节叫做魔数(Magic Number),它的唯一作用就是确定这是一个可以被 JVM 接受的 Class 文件。
Class 文件中没有任何分隔符, 各数据项严丝合缝依次排列。哪个字节代表什么含义,谁挨着谁,长度是多少,都是有规定的,不能改变。
类文件结构
先来看一个 Class 文件的标准结构:
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
来自《The Java Virtual Machine Specification》Java SE 8 Edition
如上所示,Class 文件中包含两种数据类型:「无符号数」和「表」。
无符号数是基本类型,u1、 u2、 u4、 u8 分别表示 1 个字节、 2 个字节、 4 个字节和 8 个字节的无符号数。无符号数可以描述数字、索引引用、数量值或者按照 UTF-8 编码的字符串。
表是由多个无符号数或其他表组合而成(复杂对象),并且以「_info」结尾。一个 Class 文件可以视作一张表。
各数据项说明:
名称 |
类型 |
数量 |
说明 |
magic |
u4 |
1 |
魔数:CAFE BABE |
minor_version |
u2 |
1 |
次版本 |
major_version |
u2 |
1 |
主版本 |
constant_pool_count |
u2 |
1 |
常量池计数 |
constant_pool |
cp_info |
constant_pool_count - 1 |
常量池 |
access_flags |
u2 |
1 |
访问标志 |
this_class |
u2 |
1 |
类索引 |
super_class |
u2 |
1 |
父类索引 |
interfaces_cout |
u2 |
1 |
接口索引计数 |
interfaces |
u2 |
interfaces_count |
接口索引集合 |
fields_count |
u2 |
1 |
字段计数 |
fields |
field_info |
fields_count |
字段集合 |
methods_count |
u2 |
1 |
方法计数 |
methods |
method_info |
methods_count |
方法集合 |
attributes_count |
u2 |
1 |
属性计数 |
attributes |
attribute_info |
attributes_count |
属性集合 |
顺序由上到下,严格限制,不可修改
下面用一张更形象的图展示一下 Class 文件的结构示意:
Class 文件结构示意
如何查看
我们可以通过 JDK 自带的「javap」命令来查看 Class 文件的字节码信息:
javap -v User.class
你会看到如下信息:
通过命令查看字节码
另外,还可以通过 IDEA 的插件来查看,会更加友好一些。效果如下:
通过插件查看字节码
插件:JClassLib
结束
学习字节码会让之前只知其然的知识,变得知其所以然。