带你分析字节码-深入理解class(一)

class文件是编译器编译之后供虚拟机解释执行的二进制字节码文件,不只是java,
比如Groovy,JRuby,FANTOM,Jython,和Scala。这是在jvm上运行的5大脚本语言。下面就带你分析一个class的字节码文件,class文件只有无符号数和表两种基本的数据类型,u1,u2,u4,u8代表1个字节,2个字节,4个字节,8个字节的无符号数。表是由无符号数和表组合而成的结构,说表觉得不好理解的话,可以认为就是按照规定排列无符号数来描述的一种结构。
编译版本:jdk1.7
class打开工具:16进制编辑器winHex
编译代码:

import java.io.Serializable;


public class Amethod implements Serializable{
    private int j;
    public int getJ(){
        return j;
    }
}

class文件打开:
带你分析字节码-深入理解class(一)_第1张图片
从这里我们可以看到,class以8个字节为基础单位,无任何分隔符

常量池:使用命令 javap -verbose Amethod,常量池只分析一个(其他的分析方法一致),其他的就通过这个命令来看
带你分析字节码-深入理解class(一)_第2张图片

1.魔数
开头的cafebabe(咖啡宝贝?java的杯子装咖啡的logo),表明这是一个可以被jvm执行的class文件,仅仅通过后缀名来辨别文件的类型是不安全的,因为你可以随意修改后缀名,jpg等等也有自己的魔数,可以打开看一下:
带你分析字节码-深入理解class(一)_第3张图片
因为有魔数的存在,即使*.jpg被修改成了*.1234567,仍然可以用打开。
2.版本号
紧接着的00000033代表这个class文件被编译的版本号,0000是次版本号,0033是主版本号,33转换成10进制就是51,jdk1.7可以编译的最大版本号就是51。虚拟机只能编译版本号比自己支持的最大版本号小的,超过了虚拟机支持的最大版本号,会抛出java.lang.UnsupportedClassVersionError。
3.常量池
接着就是常量池,常量池里存放的是字面常量(literal)和符号引用(symbolic references),字面量比较接近java中常量的概念,如文本字符串,声明为final的常量值等。符号引用属于编译原理方便的概念,包含以下三个常量;
3.1 类和接口的全限定名
3.2 字段的名称和描述符
3.3 方法的名称和描述符

每一个常量都是一个表,或者说都有他们自己的结构,所有常量结构的共同点是它们的第一位都是一个u1类型的标志位,用来标志这个常量的类型。我们来看这个具体的例子:
0x0018 :用来指明常量的个数,每个class的常量个数都不一样,需要指明,这里表示有23个常量。为什么不是24,0位空出来了表示null,把不指向任何值的常量池的索引指向这个null。

带你分析字节码-深入理解class(一)_第4张图片

继续分析,接下来是07,这个是常量的标志类型,查上表标志是7的,我们看到这个类或接口的符号引用,CONSTANT_Class_info,以_info结尾的表示这是个表,表结构如下:
带你分析字节码-深入理解class(一)_第5张图片
tag:07是标志位,占一个字节,name_index占两个字节,那么从07开始向后看两个字节,0002,表示指向常量池中的第二个常量,紧接着的就是第二个常量,那么继续,01,标志位是01的查表可知是UTF-8编码的字符串,CONSTANT_UTF8_INFO,以_info结尾,也是一个表,表结构如下:
带你分析字节码-深入理解class(一)_第6张图片
tag:01,占一个字节
length:类型是u2,表示占两个字节,01后面的两个字节是0007,表示长度为7,
bytes:以utf-8缩略码表示的字符串,占一个字节,数量为length,length我们已经知道是7,那么后面的7个字节就表示这个常量的值,向后看7个字节:
41 6D 65 74 68 6F 64,查ascii码表16进制:
41:A
6D:M
65:E
74:T
68:H
6F:O
64:D
合起来就是Amethod,是不是类的全限定名!这里我没有将Amethod.java放在包下面,所以全限定名就是Amethod,假如我把这个类放在com.txx.csdn这个包下面,那么这个值就应该是com/txx/csdn/Amethod

到此为止分析了常量池中的第一个常量和第二个常量,后面的都可以这样类比,其他的常量池类型的结构请大家自行百度,这里就不贴了。

你可能感兴趣的:(字节码分析)