Java语言从诞生之时就宣称一次编写,到处运行的跨平台特性,其实现原理是源码文件并没有直接编译成机器指令,而是编译成Java虚拟机可以识别和运行的字节码文件(Class类文件,*.class),字节码文件是一种平台无关的中间编译结果,字节码文件由java虚拟机读取,解析和执行,java虚拟机屏蔽了不同操作系统和硬件平台的差异性。

如今的java虚拟机已经称为一种通用平台,不但能够运行java语言,Groovy,JRuby,Jython等一大批动态语言也可以直接在Java虚拟机上运行,其原理也是这些动态语言的编译器将源码文件编译为和Java相同的字节码文件,这样Java虚拟机就可以像执行java语言一样执行这些动态语言了。

字节码class类文件是由一系列字节码命令组成,用于表示程序中各种常量、变量、关键字和运算符号的语义等等。Java的Class类文件是一组以8为字节为单位的二进制流,各个数据项严格按照顺序紧凑地排列在Class类文件之中,中间没有添加任何分隔符,当遇到需要占用8位字节以上空间的数据项时,按照高位在前的方式分割成若干个8位字节进行存储。

Java虚拟机规定,Class类文件格式采用类似C语言结构体的伪结构来存储,这种伪结构中只有两种数据类型:无符号数和表:

(1).无符号数:

属于基本类型的数据,以u1, u2, u4, u8来分别代表1个字节,2个字节,4个字节和8个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照UTF-8编码的字符串值。

(2).表:

由多个无符号数或其他表作为数据项构成的复合数据类型,所以表都习惯性地以“_info“结尾。表用于描述有层次关系的复合结构数据,整个Class文件本质就是一张表。

Java Class类文件结构如下:

类型

名称

数量

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

attributes_count

1

attribute_info

attributes

attributes_count

Class类文件没有任何分隔符,是严格按照这个结构表顺序排列,下面具体介绍各个名称含义:

(1).magic:

每个Class文件的头4个字节被称为魔数,它的唯一作用是用于确定这个文件是否为一个能被java虚拟机所接收的Class类文件,即用于判定文件是否是符合规范的java Class文件。虽然说后缀名“.class”可以表明文件是一个Class文件,但是文件后缀名是可以随意被改动的,基于安全的考虑,很多文件都通过魔数值来唯一确定文件类型,java的Class文件魔数是:0xCAFEBABE.

(2).minor_version和major_version:

每个Class文件的第5和第6个字节代表Class文件的次版本号,第7和第8个字节代表Class文件的主版本号。

Class文件的主、次版本号是由JDK决定的,JDK1.0~JDK1.1使用了45.0~45.3的版本号(45是主版本好,点”.“之后的是次版本号),从JDK1.1开始,每个大版本的JDK主版本号加1.

Class主、次版本号的一个作用时,高版本的Java虚拟机可以向前兼容,运行低版本JDK编译的Class字节码文件,而低版本的java虚拟机不能运行高版本JDK编译的Class字节码文件。当低版本的java虚拟机运行高版本JDK编译的Class字节码文件时,通常会报类似如下的异常:

  1. Exception in thread "main" java.lang.UnsupportedClassVersionError: a (Unsupporte
  2. d major.minor version 49.0)

JDK1.0~JDK1.1使用了45.0~45.3的版本号,JDK1.2使用了46.0~46.65535的版本号,JDK1.3使用了47.0~47.65535的版本号,JDK1.4使用了48.0~48.65535的版本号,JDK1.5使用了49.0~49.65535的版本号,JDK1.6使用了50.0~50.65535的版本号,JDK1.7使用51.0~51.65535的版本号。

在编译时可以通过指定-target参数来改变主版本号,如JDK1.6编译时如果没有给定target参数,则编译出来的Class文件的主版本号是50,如果给定”-target 1.4 -source 1.4”参数之后,则主版本将变为48,如果给定”-target 1.5 ”参数之后,则主版本将变为49。

(3). constant_pool_count和constant_pool:

constant_pool_count代表Class文件中常量池的数目,由于常量池的计数从1开始,因此常量池的容量是constant_pool_count-1。

第0项常量空出做特殊考虑,为了满足一些指向常量池的索引值在某些特定情况下需要表达“不指向任何一个常量池”的意思。

constant_pool常量池是Class类文件中出现的第一个表类型数据,常量池主要存放两大类常量:

a.字面量(Literal):包括文本字符串、final类型常量值。

b.符号引用(SymbolicReferences):包括类和接口的全限定名、字段的名称和描述符、方 法的名称和描述符。

(4). access_flags:

用于表示Class或接口层次的访问标志,即类或接口层面的访问控制信息,通常存储的信息包括:Class类文件是类、接口、枚举或是注解;是否定义为public类型;是否定义为abstract类型;类是否被定义为final等等。

(5). this_class、super_class和interfaces:

this_class类索引用于确定类的全限定名,super_class父类索引用于确定父类的全限定名,interfaces接口索引用于确定接口的全限定名,由于java中可以实现多个接口,因此使用interfaces_count来存储接口数量。

(6). field:

field_info字段表用于描述接口或者类中声明的变量,field字段包括了类级变量(静态变量)和实例级变量(成员变量),但不包括方法内部的局部变量。

fields_count字段数目表示Class文件中的类和实例变量总数,字段存放的信息包括:字段访问标志、是否静态、是否final、是否并发可见volatile、是否可序列化transient、数据类型、字段名称等等。

注意:字段表中不包含从父类或者接口中继承而来的字段,但是会添加原本代码中不存在的字段,例如this,以及内部类对外部类访问而自动添加的外部类实例字段等。

(7).method:

method_info方法表用于描述类或者接口中声明的方法,methods_count用于表示Class文件中方法总数,method方法存储了方法的访问标识、是否静态、是否final、是否同步synchronized、是否本地方法native、是否抽象方法abstract、方法返回值类型、方法名称、方法参数列表等信息。

方法的代码指令并没有直接存放在方法表中,而是存放着属性表中的方法表Code中。

注意:如果父类的方法在子类没有被重写,方法表中不会出现来自父类的方法信息,但是编译器会自动添加类构造器”<clinit>”方法和实例构造器”<init>”方法。

Java编译器的方法特征签名只包括:方法名称、参数顺序和参数类型,不包括方法返回值类型,因此java的方法重载不能通过方法的返回值类区别,但是在Class文件中,方法特征签名包括方法的返回值类型,因此Class文件中可以共存两个名称和参数完全相同而返回值类型不同的方法。

(8). attribute:

attribute_info属性表是Class文件格式中最具扩展性的一种数据项目,用于存放field_info字段表、method_info方法表以及Class文件的专有信息,属性表不要求各个属性有严格顺序,只要求不与已有的属性名字重复即可,属性表中存放的常用信息如下:

属性名称

使用位置

含义

Code

方法表

Java代码编译后的字节码指令

ConstantValue

字段表

final关键字定义的常量值

Deprecated

类、方法表、字段表

被声明为Deprecated的字段或方法

Exception

方法表

方法抛出的异常

InnerClasses

类文件

内部类列表

LineNumberTable

Code属性

java源码行号和字节码指令的对应关系

LocalVariableTable

Code属性

方法的局部变量描述

SourceFile

类文件

源文件名称

Synthetic

类、方法表、字段表

标识方法或字段为编译器自动生成

Class文件是二进制文件,使用支持二进制的文本编辑器打开之后显示的全是二进制数据,非常的不便于阅读和理解,使用JDK提供的javap工具可以简单将Class反编译,编译理解Class文件的结构,例子如下:

源码:

  1. public class Test {
  2. public int getNum(int i) {
  3. return i + 1;
  4. }
  5. }

javap反编译之后的字节码文件:

  1. public class Test extends java.lang.Object
  2. SourceFile: "Test.java"
  3. minor version: 0
  4. major version: 50
  5. //常量池
  6. Constant pool:
  7. const #1 = class        #2;
  8. const #2 = Asciz        Test;
  9. const #3 = class        #4;
  10. const #4 = Asciz        java/lang/Object;
  11. const #5 = Asciz        <init>;  //实例构造器
  12. const #6 = Asciz        ()V;  //void返回类型
  13. const #7 = Asciz        Code;  //属性表Code属性
  14. const #8 = Method       #3.#9;  //方法特征签名  java/lang/Object."<init>":()V
  15. const #9 = NameAndType  #5:#6;//  方法名称和返回值"<init>":()V
  16. const #10 = Asciz       LineNumberTable;  //属性表源码行号和字节码指令对应表
  17. const #11 = Asciz       LocalVariableTable;  //属性表方法局部变量表
  18. const #12 = Asciz       this;  //Test类实例对象本身
  19. const #13 = Asciz       LTest;;  //对象类型,Test类
  20. const #14 = Asciz       getNum;  //方法名称
  21. const #15 = Asciz       (I)I;  //方法参数列表为一个int类型和返回值为int类型
  22. const #16 = Asciz       i;  //参数名称i
  23. const #17 = Asciz       I;  //参数类型int
  24. const #18 = Asciz       SourceFile;
  25. const #19 = Asciz       Test.java;
  26. //方法表
  27. {
  28. //构造函数(默认构造方法)
  29. public Test();
  30. Code:  //属性表Code属性
  31. Stack=1, Locals=1, Args_size=1
  32. 0:   aload_0
  33. 1:   invokespecial   #8; //Method java/lang/Object."<init>":()V
  34. 4:   return
  35. LineNumberTable:
  36. line 2: 0
  37. LocalVariableTable:  //属性表方法局部变量表
  38. Start  Length  Slot  Name   Signature
  39. 0      5      0    this       LTest;
  40. //自定义方法
  41. public int getNum(int);
  42. Code:
  43. Stack=2, Locals=2, Args_size=2
  44. 0:   iload_1
  45. 1:   iconst_1
  46. 2:   iadd
  47. 3:   ireturn
  48. LineNumberTable:
  49. line 4: 0
  50. LocalVariableTable:
  51. Start  Length  Slot  Name   Signature
  52. 0      4      0    this       LTest;
  53. 0      4      1    i       I
  54. }

(转)《深入理解java虚拟机》学习笔记5——Java Class类文件结构的更多相关文章

  1. 深入理解Java虚拟机学习笔记(三)-----类文件结构/虚拟机类加载机制

    第6章 类文件结构 1. 无关性 各种不同平台的虚拟机与所有平台都统一使用的程序存储格式——字节码(即扩展名为 .class 的文件) 是构成平台无关性的基石. 字节码(即扩展名为 .class 的文 ...

  2. 《深入Java虚拟机学习笔记》- 第19章 方法的调用与返回

    <深入Java虚拟机学习笔记>- 第19章 方法的调用与返回

  3. 《深入Java虚拟机学习笔记》- 第16章 控制流

    <深入Java虚拟机学习笔记>- 第16章 控制流

  4. 《深入Java虚拟机学习笔记》- 第17章 异常

    <深入Java虚拟机学习笔记>- 第17章 异常

  5. 《深入Java虚拟机学习笔记》- 第13章 逻辑运算

    <深入Java虚拟机学习笔记>- 第13章 浮点运算

  6. 《深入Java虚拟机学习笔记》- 第14章 浮点运算

    <深入Java虚拟机学习笔记>- 第13章 浮点运算

  7. 《深入Java虚拟机学习笔记》- 第8章 连接模型

    Java虚拟机学习笔记(八)连接模型

  8. 《深入Java虚拟机学习笔记》- 第4章 网络移动性

    Java虚拟机学习笔记(四)网络移动性

  9. 《深入Java虚拟机学习笔记》- 第2章 平台无关

    Java虚拟机学习笔记(二)平台无关

  10. Java虚拟机学习笔记——JVM垃圾回收机制

    Java虚拟机学习笔记——JVM垃圾回收机制 Java垃圾回收基于虚拟机的自动内存管理机制,我们不需要为每一个对象进行释放内存,不容易发生内存泄漏和内存溢出问题. 但是自动内存管理机制不是万能药,我们 ...

随机推荐

  1. hello world from hibernate

    初次学习hibernate,第一个程序,用的是XML配置文件 准备工作:下载hibernate的JAR包,我下的是当前最新的4.2.3,SQL的数据库驱动文件SQLJDBC,导入上述架包.

  2. 深入理解计算机系统第二版习题解答CSAPP 2.20

    T2Uw(w)=x, x≥0时 T2Uw(w)=x+2w, x<0时 利用上面的公式,重新计算2.19的问题.

  3. LeetCode 344

    Reverse String Write a function that takes a string as input and returns the string reversed. Exampl ...

  4. CSS3—3D翻转

    本案例主要是css3和html5,不会js也可以做动画◕.◕ 一.首先看下主要需要的样式: perspective transform transition position classList 就这 ...

  5. HTML5와 CSS3 적용기

    HTML5의 DTD 선언 <!DOCTYPE html>  HTML5의 인코딩 선언 <meta charset="utf-8">  그리고나서는 새로 ...

  6. Tomcat - 持久化 Session

    Session 是保存在内存中的,如果服务器重启.宕机的话,Session 就会丢失.有时候,我们需要对 Session 持久化以应对意外的情况发生.例如,客户端与服务器在交互过程中,可能因为 Ses ...

  7. windows编程socket问题

    今天调试了个MFC网络程序,被bug困扰了一天,终于在收工前解决了. 大致是这样的,我们需要用上位机远程控制机器车前行.上位机上的MFC app的键盘按键响应如下:当按键按下时,系统会发送一个消息给a ...

  8. SQL Server 查询分析器提供的所有键盘快捷方式(转)

    下表列出 SQL Server 查询分析器提供的所有键盘快捷方式. 活动 快捷方式 书签:清除所有书签. CTRL-SHIFT-F2 书签:插入或删除书签(切换). CTRL+F2 书签:移动到下一个 ...

  9. [转载]IIS下开启php扩展失效? 感谢作者 本人泪流满面

    用户反应,空间不支持GD.系统环境是IIS PHP.   先用phpinfo探了一下,确实没有找到gd的影子.然后检查php.ini,发现gd扩展没有开启(windows下安装的php,其所有php扩 ...

  10. 【转】简单理解socket

    题外话 前几天和朋友聊天,朋友问我怎么最近不写博客了,一个是因为最近在忙着公司使用的一些控件的开发,浏览器兼容性搞死人:但主要是因为这段时间一直在看html5的东西,看到web socket时觉得很有 ...