Java的 class文件结构

标签: java class 文件结构 | 发表时间:2015-11-24 11:51 | 作者:qq466862016
出处:http://www.iteye.com

Java-class文件结构

一、概述

       我们都知道我们现在写的源代码计算机是不认识的,我们需要根据指定的编译器进行编译-连接-执行,这样才是我们想要的结果,所以计算机只能认识0或者1 ,那么如何与操作系统或者机器指令无关的程序能执行,那么在操作系统以及机器指令之上的那就是虚拟机了,这样我们编写的代码不再是最终形成二进制本地指令代码,而是一种在操作系统和机器指令之上的虚拟机规定的文件格式。这也说明了java是一次编写到处运行的由来,但是并不是到处运行的,运行的前提是虚拟机是否此操作系统支持。那么我们的JVM也要所规定class文件的格式,它不管你是什么语言编写并编译出来的class文件,必须严格符合JVM定义的格式,否则JVM不会进行加载的。也有点像我们做TCP UDP定义的消息格式: (比如:我们定义tcp消息格式为:消息头4个字节+不定长度的消息体) 。

 

        class文件是一组以8位字节为基础单位二进制流,各个数据项严格按照顺序紧凑的排列在class文件中,中间不添加任何空格。这样看起来整个class文件中的所有数据都是运行数据,没有空隙存在。如果遇到占用大于8位字节以上的空间的数据项的时候,会以8位字节为单位高位在前低位在后的顺序排列进行存储。class文件结构采用类似c语言伪结构来存储 这种伪结构有两种数据类型:无符号数和表 ,解析都是根据这两个数据类型来解析的。

二、class 文件魔数和版本号

 

 

 

 

     每个class文件的头都包含有4个字节的魔数 他是唯一作用是为了确定这个文件是否确定被JVM所接受,也就是身份识别的作用。 魔数值为:0XCAFEBABE (四个字节)   紧接着是:第五个和第六个字节为次版本号 第七和第八个字节为主版本号比如: 0XCAFEBABE00000032  次版本为0 主版本号为java1.7 

jdk1.0 从45.0开始的 具体版本号查看文档。

三、常量池

在紧跟着版本号后跟着常量池的入口,常量池的u2类型的数据代表池容量计数值 

0XCAFEBABE 00000032 0017

0017 偏移量不是从0开始的而是从1开始的  0x0017 十进制为23 代表有 22个常量,索引从1~22 第0个做特殊处理。常量池中两类常量:字面量和符号引用

字面量:字符别声明为final的常量值等 。

符号引用:

1、 类或者接口的全限定名

2、 方法的签名

3、 字段的签名

这样虚拟机运行的时候会从常量池中获取对应的符号引用。

我们接着往后继续分析class文件内部结构,常量池中的每一项开始都包含有一个u1的tag + 对应数据项,在常量池中一共包含有11中常量项类型:



 

 我们开始分析常量池中的第一项  tag 一个u1 为0x07 十进制为7 代码类型为7的Class_info 

 

 class_info 项 为:一个u1 的tag + 一个u2的指向第几个常量项

0X0002 指的是第二项, 第二项tag为 0x01 为类型为1的UTF8_INFO utf8_info 类型为

一个u1的tag 一个u2的bytelength  + length 个u1的数据区



 

 往后面常量池数据项的对照着类型表依次分析这样太麻烦了。我们可以通过java 中自带的命令进行分析 在jdk中的bin目录下包含有javap 命令  格式为:javap -verbose xxx.class

 我们首先写个java类进行测试

package clazz;

public class MyClazz {

	private String name;
	private int age;
	
	
	
	
	
	public String getName() {
		return name;
	}

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

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public void say(String name) {
		
		System.err.println("name");
	}
	
	public String getSay(String name) {
		
		return name;
	}
}

 

执行命令 javap -verbose MyClazz.class 

 

D:\work\ewp\test\bin\clazz>javap -verbose MyClazz.class
Classfile /D:/work/ewp/test/bin/clazz/MyClazz.class
  Last modified 2015-11-24; size 1017 bytes
  MD5 checksum 1899fd38b93905c461f55b955076f985
  Compiled from "MyClazz.java"
public class clazz.MyClazz
  SourceFile: "MyClazz.java"
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Class              #2             //  clazz/MyClazz
   #2 = Utf8               clazz/MyClazz
   #3 = Class              #4             //  java/lang/Object
   #4 = Utf8               java/lang/Object
   #5 = Utf8               name
   #6 = Utf8               Ljava/lang/String;
   #7 = Utf8               age
   #8 = Utf8               I
   #9 = Utf8               <init>
  #10 = Utf8               ()V
  #11 = Utf8               Code
  #12 = Methodref          #3.#13         //  java/lang/Object."<init>":()V
  #13 = NameAndType        #9:#10         //  "<init>":()V
  #14 = Utf8               LineNumberTable
  #15 = Utf8               LocalVariableTable
  #16 = Utf8               this
  #17 = Utf8               Lclazz/MyClazz;
  #18 = Utf8               getName
  #19 = Utf8               ()Ljava/lang/String;
  #20 = Fieldref           #1.#21         //  clazz/MyClazz.name:Ljava/lang/String;
  #21 = NameAndType        #5:#6          //  name:Ljava/lang/String;
  #22 = Utf8               setName
  #23 = Utf8               (Ljava/lang/String;)V
  #24 = Utf8               getAge
  #25 = Utf8               ()I
  #26 = Fieldref           #1.#27         //  clazz/MyClazz.age:I
  #27 = NameAndType        #7:#8          //  age:I
  #28 = Utf8               setAge
  #29 = Utf8               (I)V
  #30 = Utf8               say
  #31 = Fieldref           #32.#34        //  java/lang/System.err:Ljava/io/PrintStream
  #32 = Class              #33            //  java/lang/System
  #33 = Utf8               java/lang/System
  #34 = NameAndType        #35:#36        //  err:Ljava/io/PrintStream;
  #35 = Utf8               err
  #36 = Utf8               Ljava/io/PrintStream;
  #37 = String             #5             //  name
  #38 = Methodref          #39.#41        //  java/io/PrintStream.println:(Ljava/lang/S
  #39 = Class              #40            //  java/io/PrintStream
  #40 = Utf8               java/io/PrintStream
  #41 = NameAndType        #42:#23        //  println:(Ljava/lang/String;)V
  #42 = Utf8               println
  #43 = Utf8               getSay
  #44 = Utf8               (Ljava/lang/String;)Ljava/lang/String;
  #45 = Utf8               SourceFile
  #46 = Utf8               MyClazz.java
{
  public clazz.MyClazz();
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #12                 // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       5     0  this   Lclazz/MyClazz;

  public java.lang.String getName();
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #20                 // Field name:Ljava/lang/String;
         4: areturn
      LineNumberTable:
        line 13: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       5     0  this   Lclazz/MyClazz;

  public void setName(java.lang.String);
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: putfield      #20                 // Field name:Ljava/lang/String;
         5: return
      LineNumberTable:
        line 17: 0
        line 18: 5
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       6     0  this   Lclazz/MyClazz;
               0       6     1  name   Ljava/lang/String;

  public int getAge();
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #26                 // Field age:I
         4: ireturn
      LineNumberTable:
        line 21: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       5     0  this   Lclazz/MyClazz;

  public void setAge(int);
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: iload_1
         2: putfield      #26                 // Field age:I
         5: return
      LineNumberTable:
        line 25: 0
        line 26: 5
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       6     0  this   Lclazz/MyClazz;
               0       6     1   age   I

  public void say(java.lang.String);
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: getstatic     #31                 // Field java/lang/System.err:Ljava/io/Pr
         3: ldc           #37                 // String name
         5: invokevirtual #38                 // Method java/io/PrintStream.println:(Lj
         8: return
      LineNumberTable:
        line 30: 0
        line 31: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       9     0  this   Lclazz/MyClazz;
               0       9     1  name   Ljava/lang/String;

  public java.lang.String getSay(java.lang.String);
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=2, args_size=2
         0: aload_1
         1: areturn
      LineNumberTable:
        line 35: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       2     0  this   Lclazz/MyClazz;
               0       2     1  name   Ljava/lang/String;
}

D:\work\ewp\test\bin\clazz>

  从结果上看 我们可以看到 46个常量项 与我们在前面算的一个 0X2F 十进制为47  0项不算 1~46 正好

对应。

常量池后面仅接着是访问修饰下面是对应修饰表



 我们的public +jdk1.2之后  0x0020 | 0x001 那结果就是 0x0021 



 和我们用javap 命令查看的一样 

  flags: ACC_PUBLIC, ACC_SUPER

接着修饰后面的是类索引 + 父索引 + 接口集合索引

类索引 this_class 一个u2 指向常量池中class_info项的引用地址

 

java 是只允许单继承不允许多继承的 一个u2的父类索引 指向常量池中class_info项的引用地址

下面是this_class 和父类索引 指向图

 



 0x001 指向常量池中第一项  0X003 指向常量池中的第三项 (注意他们都是指向class_info 类型)

 

 

接口集合索引是一组u2的集合

在此class中 0X0000 此接口集合索引为0 

跟在后面的是 字段表集合

字段描述包括是类级别还是实例级别、作用域、是否为安全的、是否修饰为static  是否可变等

 

字段表结构



 

 

比如: private String name,sex; 定义   

attr = 2 



 都是指向常量池中的引用地址



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [java class 文件结构] 推荐:

Java的 class文件结构

- - Java - 编程语言 - ITeye博客
Java-class文件结构.        我们都知道我们现在写的源代码计算机是不认识的,我们需要根据指定的编译器进行编译-连接-执行,这样才是我们想要的结果,所以计算机只能认识0或者1 ,那么如何与操作系统或者机器指令无关的程序能执行,那么在操作系统以及机器指令之上的那就是虚拟机了,这样我们编写的代码不再是最终形成二进制本地指令代码,而是一种在操作系统和机器指令之上的虚拟机规定的文件格式.

分析一个Java Class文件

- - CSDN博客推荐文章
Java源码文件TestClass.java:. 展示这个Class文件的16进制内容:. 00 00 00 34 : 版本号是1.8.0;. 00 16: 说明常量池有21个常量,1-21, index留做他用;接下来就是分别这21个常量的描述:. 07/00 02 :CONSTANT_Class_info 常量,类名索引是该常量池的第2项;.

Java编程的动态特性, 从Reflection到Runtime Class Transformation

- - 编程语言 - ITeye博客
关于Java编程的动态特性,从认识的过程上要从 Reflection 到 instrumentation. 初步的开发者刚接触到Reflection会非常兴奋,因为反射可以在运行时完成很多之前不可能的任务,这件利器使人打破了很多束缚. Java Annotation出现后,更让Java变得更加有活力,更加友好.

java读取指定package下的所有class

- - 互联网 - ITeye博客
之前在看spring注解的时候,有看到再配置文件里面定义component scan package就能自动扫描对应包下面的class,然后根据注解生成相应的bean. 自己对这个功能很好奇,就搜了下,找到了实现的关键代码,记录下. * 从包package中获取所有的Class. // 第一个class类的集合.

Django class-based view 基础

- Ken - python.cn(jobs, news)
自从Django在1.3中新增了class-based view以来,还没有仔细研究它,开始感觉这个东西是否有点多余. 因为Django已经有了Generic veiws了啊, 可是仔细看过class-based veiw之后, 这种想法打消了, 因为你完全可以用类方法实现你所有的视图, 而代码阅读起来却更容易!.

Django class-based view 深入

- Ken - python.cn(jobs, news)
上一篇我们粗略介绍了Django中的class-based view基础知识, 本篇我们继续来看关于class-based view的高级应用.. 我们继续沿用上篇中的model:. 我们来看看如何对一个Book实例进行更新, 我们要做的只是在视图类中更新 :.     template_name = 'updatebook.html'  #这里是你的模板文件名.

标签?ID?还是CLASS?

- - 前端观察
想谈一下几个基本的HTML问题,都是围绕着应该怎样使用HTML. 多用有语义的标签,少用div和span,避免使用没有class的div和span. 设想一下HTML的世界最初只有div和span这两个标签,其实网页依然可以写得出来. 更多标签的出现,其实是为了替代利用率高但不好书写的 
 和  来的.

Linux 文件结构

- Shiina Luce - OSMSG
想了解 Linux 文件系统树形结构,却又不愿翻阅 FHS 的朋友,可以参考 skill2die4 制作的这张简图. 此图算是 FHS 的图形化版本,简要的说明了 Linux 系统中各个目录的用途及层级关系,适合初学者使用参考. 不过其中较新的如 /run 目录并未在其中出现. 做为参考,这是 Fedora 16 Beta i686 上的文件结构:.

偏好的 JavaScript Class 實作『基本款』

- Alu - Fred&#39;s blog
最近有一些想法,便和伙伴在空閒時間寫一些 Prototype 的專案,既然是嘗試形態的專案開發,就不必考慮穩定性和熟悉度,也不用顧慮失敗的問題,可以盡情玩弄新的,或是過去不常使用的技術. 所以,這次使用了 nodejs + express + HTML5 來開發,大玩 JavaScript. JavaScript 雖然看起來像 Java,但畢竟它不是真的 Java,很多功能考量以易於使用為優先,所以並不嚴謹,一些功能也因此被拿掉.