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
都是指向常量池中的引用地址
相关推荐
#### 二、Class文件的基本结构 ##### 1. 魔数与版本号 - **魔数**:每个`.class`文件的开头四个字节称为“魔数”,其值固定为`CAFEBABE`。这是为了确认该文件是否为有效的`.class`文件,以及防止由于文件损坏等原因...
### JavaClass文件的结构分析及其校验 #### 引言 随着Java技术的不断发展与广泛应用,JavaClass文件作为Java程序的基础组成部分,其结构与验证机制的重要性日益凸显。本文旨在深入探讨JavaClass文件的结构特点,并...
Java Class文件是Java程序经过编译后的二进制表示形式,它是Java虚拟机(JVM)执行的基础。本文将深入探讨Java类文件的结构、编译过程以及如何使用提供的工具进行查看。 一、Java类文件结构 Java源代码(.java文件...
首先,让我们深入了解一下Java Class文件的结构。一个.class文件包含了Java程序的基本结构单元,如类、接口、字段和方法定义。它采用特定的字节码格式,使得虚拟机(JVM)能够理解并执行其中的指令。这些字节码由一...
Java Class文件是Java源代码经过编译器处理后的二进制形式,它们包含了程序的结构信息,如类、接口、变量、方法等定义。在Java开发过程中,有时我们需要查看或分析这些Class文件,以理解程序运行原理、调试或逆向...
1、打开一个或者多个*.class文件,XJad反编译后,重命名为*.java文件, 保存至当前文件夹,并在编辑器中打开查看; 2、打开一个文件夹,XJad将该文件夹下所有*.class文件进行反编译,并保存至该文件夹下, 依据包...
Java Class文件是Java程序编译后的二进制格式,它包含了类和接口的定义、方法体、常量池等信息,但这些信息是以机器可读的字节码形式存在,对于人类来说不易理解。为了查看和理解Class文件内部的源代码,我们就需要...
JavaClassViewer-2.0.4, 用于理解Class文件结构。 解析出class结构,以及十六进制的表示,特别方便。 原下载地址: http://www.softpedia.com/get/Programming/File-Editors/Java-Class-Viewer.shtml。另外也可以参考...
一、Java Class文件结构 Java Class文件是Java虚拟机(JVM)执行的二进制格式,它包含了类的元数据、方法和字段定义等信息。Class文件由一系列8位字节构成,遵循特定的布局规则。每个Class文件都以魔数(Magic ...
Java Class反编译工具是程序员在处理已编译的字节码文件时不可或缺的辅助工具。这类工具的主要功能是将`.class`文件转换回可读性强的`.java`源代码,帮助开发者理解或修改已有的Java程序,尤其在没有源代码的情况下...
首先,让我们详细了解一下Java class文件的结构。一个class文件由一系列字节组成,这些字节按照特定的格式排列,包括魔数、版本信息、常量池、访问标志、类和父类索引、接口索引集合、字段表集合、方法表集合以及...
本篇文章将详细讲解如何使用指定的"电脑java class文件查看与修改工具"来查看和修改这些文件。 首先,我们要了解的是`jd-gui查看.exe`。这是一个名为JD-GUI的开源工具,它允许开发者直观地查看Java字节码(.class...
### Java反编译工具:将.class文件转换为.java文件 #### 概述 在软件开发领域,有时我们可能需要分析第三方库或者开源项目的内部结构,这时就需要借助于反编译工具来帮助我们阅读和理解其源码。Java作为一种广泛...
在深入了解Class文件的结构之前,我们需要明确Class文件在Java生态系统中的重要地位。Java程序被编译器编译后生成的`.class`文件,实际上是字节码文件,它们是Java虚拟机(JVM)执行的基本单位。通过对Class文件结构...
Java Class文件编辑器,如JD-GUI 0.3.3 绿色版,是一种用于查看和理解Java字节码的工具,对于开发者而言尤其重要,因为它提供了将已编译的`.class`文件反编译回源代码的能力。在深入讨论这个工具之前,我们先来了解...
**JAVA CLASS文件查看器** JAVA CLASS文件是Java字节码文件,它包含了Java源代码编译后的机器可读代码。这种文件格式对于开发者来说非常重要,因为它允许他们理解已编译的程序,尤其是在没有源代码的情况下。"JAVA ...
它允许用户查看并理解已编译的.class文件的内容,因为Java的源代码(.java文件)通常不会随可执行文件一起发布。`JavaDecompiler`这个标题暗示我们将探讨如何使用Java反编译器来打开和解析.class文件。 Java类文件是...