浅析class文件内部结构

最近在为工作的事情搞的很是纠结。由于前段时间在iteye上发表了一篇“应届生,求内推”的帖子,也联系了几个“内推手”。虽然后来不成功,不过还是要感谢大部分人。究其原因可能是因为我人在南昌,看到简历后觉得离工作的地方太远,所以没有发面试通知把。不过找工作是找工作,学习还是不能忘记的。下面的这篇文章,是我这几天看《深入浅出java 虚拟机》第六章 总结出来的一些自己的东西。
我在这里主要讲的是class file 文件结构。首先要给出两张表,第一张是class文件基本类型,第二张是class文件格式。
[table]
|表6-1 class文件“基本类型”
|
|u1| 1个字节,无符号类型
|
|u2| 2个字节,无符号类型
|
|u4| 4个字节,无符号类型
|
|u8| 8个字节,无符号类型|
[/table]

[table]
|表6-2 classfile表的格式
|
|类型 |名称 |数量
|
|U4 |Magic |1
|
|U2 |Minor_version |1
|
|U2 |Major_version |1
|
|U2 |Constant_pool_count |1
|
|Cp_info |Constant_pool |Constant_pool_count-1
|
|U2 |Access_flags |1
|
|U2 |This_class |1
|
|U2 |Super_class |1
|
|U2 |Interfaces_count |1
|
|U2 |Interfaces |Interfaces_count
|
|U2 |Fields_count |1
|
|Field_info |Fields |Fields_count
|
|U2 |Methods_count |1
|
|Method_info |Methods |Methods_count
|
|U2 A|ttributes_count |1
|
|Attribute_info |Attributes |Attributes_count|
[/table]对于这个表的格式,我不想全部翻译其意义,就前面几个字段的意义做一下简要说明就行了,后面的都可以“观其字面意思,就可知其内部含义”。
(1)magic(魔数)
每一个java class文件的前4个字节被称为它的魔数(magic number):0XCAFEBABE。魔数的作用在于,可以轻松地分辨出java class文件和非java class文件。如果一个文件不是以0xCAFEBABE开头。那它就肯定不是java class文件。文件格式定义者能够自由选择魔数,前提是这个选定的魔数值没有被广泛应用。当java还被称为“Oak”的时候,这个魔数就已经定下来了。一招Patrick Naughton(最初java开发小组的关键成员)的说法:“早在java第一次作为该语言的名字发布以前,我们就在寻找一些好玩的、唯一的、容易记忆的东西。选择0xCAFEBABE只不过是一个巧合,它象征着著名咖啡品牌Peet’s Coffee中深受欢迎的baristas(一种咖啡名称)。它预示了java这个名字的出现”。
(2)minor_version和major_version
Class文件的下面4个字节包含了主、次版本号。随着java技术的发展,java class文件格式可能会加入新特性。Class文件格式一旦发生变化,版本号也会随之变化。对于java虚拟机来说,版本号确定了特定的class文件格式,通常只有给定主版本号和一系列次版本号后,java虚拟机才能够读取class文件。如果class文件的版本号超出了java虚拟机所能处理的有效范围,java虚拟机将不会处理该class文件。
在sun的jdk1.0.2发布版中,java虚拟机实现支持从45.0(主版本号为45,次版本号为0)到45.3的class文件格式。在所有jdk1.1发布版中的虚拟机都能够支持版本从45.0到45.65535的class文件格式。在sun的1.2版本的sdk中,虚拟机能够支持从版本45.0到46.0的class文件格式。
1.0或1.2版本的编译器能够产生版本号为45.3的class文件。在sun的1.2版本sdk中,javac编译器默认产生版本号为45.3的class文件。但如果在javac命令行中指定了-target1.2标志,1.2版本的编译器将产生版本号为46.0的class文件。1.0或1.1版本的虚拟机上不能运行使用-target1.2标志所产生的class文件。
Java虚拟机实现的第二版中修改了对class文件主版本号和次版本号的解释。对于第二版而言,class文件的主版本号与java平台主发布版的版本号保持一致(例如:在java2平台发布版上,主版本号从45到46),次版本号与特定主平台发布版的各个发布版相关。因此,尽管不同的class文件格式可以由不同的版本号表示,但版本号不一样并不代表class文件格式不同。版本号不同的原因可能只是因为class文件由不同发布版本的java平台产生,可能class文件的格式并没有改变。
(3)constant_pool_count和constant_pool
在class文件中,魔数和版本号后面的是常量池。正如第五章中所述,常量池包含了与文件中类和接口相关的常量。常量池中存储了注入文字字符串、final变量值、类名和方法名的常量。Java虚拟机把常量池组织为入口列表的形式。在实际列表constant_pool之前,是入口在列表中的计数constant_pool_count。
(4)access_flags
紧接常量池后的两个字节称为access_flags,它展示了文件中定义的类或接口的几段信息。例如,访问标志指明文件中定义的是类还是接口;访问标志还定义了在类或接口的声明中,使用了哪种修饰符;类和接口是抽象的,还是公共的;类的类型可以为final,而final类不可能是抽象的;接口不能为final类型。

下面我根据一个实例来简要剖析class文件结构
首先,新建一个java源文件,并且编译出class文件。这个事用来做实验的,随便一点就OK了。

类名称:MyTest.java
package app;

/**
* <p>
* 这是一个测试源文件,用来生成class文件,分析class文件结构
* </p>
* @author 杨
*
*/
public class MyTest {

/**
* 属性列表
*/
private Integer id;
private String name;

/**
* 构造器
*/
public MyTest(){
this.id = 0;
this.name = "";
};

/**
* 重载构造函数
*/
public MyTest(Integer id,String name){
this.id = id;
this.name = name;
}

/**
*属性存取方法
*/
public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}


}


编译后的MyTest.class文件内容如下(以文本方式打开):
漱壕   1 )  
app/MyTest java/lang/Object id Ljava/lang/Integer; name Ljava/lang/String; <init> ()V Code



java/lang/Integer valueOf (I)Ljava/lang/Integer; LineNumberTable LocalVariableTable this Lapp/MyTest; ((Ljava/lang/Integer;Ljava/lang/String;)V getId ()Ljava/lang/Integer; setId (Ljava/lang/Integer;)V getName ()Ljava/lang/String; setName (Ljava/lang/String;)V
SourceFile MyTest.java !
I * * * Y * *+ *, / * & ! " > *+
* + # $ / * . % & > *+
2 3 ' (


其实从上面的class文件中也可以看出一点端倪出来(尽管是乱码居多)。

我们现在分析一下class文件结构对应于上面给出那张表,每个项在这个文件中所指示的具体的值。当然了,这种乱码打开的肯定不适合分析,那就以16进制方式打开把。

CA FE BA BE 00 00 00 31  00 29 07 00 02 01 00 0A
61 70 70 2F 4D 79 54 65 73 74 07 00 04 01 00 10
6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74
01 00 02 69 64 01 00 13 4C 6A 61 76 61 2F 6C 61
6E 67 2F 49 6E 74 65 67 65 72 3B 01 00 04 6E 61
6D 65 01 00 12 4C 6A 61 76 61 2F 6C 61 6E 67 2F
53 74 72 69 6E 67 3B 01 00 06 3C 69 6E 69 74 3E
01 00 03 28 29 56 01 00 04 43 6F 64 65 0A 00 03
00 0D 0C 00 09 00 0A 0A 00 0F 00 11 07 00 10 01
00 11 6A 61 76 61 2F 6C 61 6E 67 2F 49 6E 74 65
67 65 72 0C 00 12 00 13 01 00 07 76 61 6C 75 65
4F 66 01 00 16 28 49 29 4C 6A 61 76 61 2F 6C 61
6E 67 2F 49 6E 74 65 67 65 72 3B 09 00 01 00 15
0C 00 05 00 06 08 00 17 01 00 00 09 00 01 00 19
0C 00 07 00 08 01 00 0F 4C 69 6E 65 4E 75 6D 62
65 72 54 61 62 6C 65 01 00 12 4C 6F 63 61 6C 56
61 72 69 61 62 6C 65 54 61 62 6C 65 01 00 04 74
68 69 73 01 00 0C 4C 61 70 70 2F 4D 79 54 65 73
74 3B 01 00 28 28 4C 6A 61 76 61 2F 6C 61 6E 67
2F 49 6E 74 65 67 65 72 3B 4C 6A 61 76 61 2F 6C
61 6E 67 2F 53 74 72 69 6E 67 3B 29 56 01 00 05
67 65 74 49 64 01 00 15 28 29 4C 6A 61 76 61 2F
6C 61 6E 67 2F 49 6E 74 65 67 65 72 3B 01 00 05
73 65 74 49 64 01 00 16 28 4C 6A 61 76 61 2F 6C
61 6E 67 2F 49 6E 74 65 67 65 72 3B 29 56 01 00
07 67 65 74 4E 61 6D 65 01 00 14 28 29 4C 6A 61
76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 01
00 07 73 65 74 4E 61 6D 65 01 00 15 28 4C 6A 61
76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29
56 01 00 0A 53 6F 75 72 63 65 46 69 6C 65 01 00
0B 4D 79 54 65 73 74 2E 6A 61 76 61 00 21 00 01



分析:
[table]
|项目 |所占位数 |值 |说明
|
|Magic(魔数) |4 |CA FE BA BE |符合class文件格式
|
|Minor_version(次版本号) |2 |00 00 |0
|
|Major_version(主版本号) |2 |00 31 |十进制是49(JDK1.6)
|
|Constant_pool_count(常量池数) |2 |00 29 |41个
|
|Constant_pool |不定(cp_info) |后面一大串 |
[/table]

心情不好,休息。下个礼拜可能去上海找工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值