class文件中,常量池之后的相关数据解析!【class二进制文件分析】

这篇具有很好参考价值的文章主要介绍了class文件中,常量池之后的相关数据解析!【class二进制文件分析】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言:前段时间读《深入java虚拟机》介绍到class文件的时候,由于理论知识较多,人总感觉疲惫不堪,就泛泛阅读了一下。在工作中使用起来知识点知道,但是总是需要查阅各种资料。今天有时间,继续整理常量池后面的相关知识。

源码

public class Sample {
    public String m1;
    public String m2;
    public Object [] arr;

    public static void main(String[] args) {
        Sample sample = new Sample();
        sample.m1="22";
        sample.arr=new Object[12];
        System.out.println(sample.m1);
    }
}

编译之后的javap文件

  Last modified 2023-6-2; size 708 bytes
  MD5 checksum fc8bb4833223a10b68449d42080b1695
  Compiled from "Sample.java"
public class com.company.jvm.Sample
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#29         // java/lang/Object."<init>":()V
   #2 = Class              #30            // com/company/jvm/Sample
   #3 = Methodref          #2.#29         // com/company/jvm/Sample."<init>":()V
   #4 = String             #31            // 22
   #5 = Fieldref           #2.#32         // com/company/jvm/Sample.m1:Ljava/lang/String;
   #6 = Class              #33            // java/lang/Object
   #7 = Fieldref           #2.#34         // com/company/jvm/Sample.arr:[Ljava/lang/Object;
   #8 = Fieldref           #35.#36        // java/lang/System.out:Ljava/io/PrintStream;
   #9 = Methodref          #37.#38        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #10 = Utf8               m1
  #11 = Utf8               Ljava/lang/String;
  #12 = Utf8               m2
  #13 = Utf8               arr
  #14 = Utf8               [Ljava/lang/Object;
  #15 = Utf8               <init>
  #16 = Utf8               ()V
  #17 = Utf8               Code
  #18 = Utf8               LineNumberTable
  #19 = Utf8               LocalVariableTable
  #20 = Utf8               this
  #21 = Utf8               Lcom/company/jvm/Sample;
  #22 = Utf8               main
  #23 = Utf8               ([Ljava/lang/String;)V
  #24 = Utf8               args
  #25 = Utf8               [Ljava/lang/String;
  #26 = Utf8               sample
  #27 = Utf8               SourceFile
  #28 = Utf8               Sample.java
  #29 = NameAndType        #15:#16        // "<init>":()V
  #30 = Utf8               com/company/jvm/Sample
  #31 = Utf8               22
  #32 = NameAndType        #10:#11        // m1:Ljava/lang/String;
  #33 = Utf8               java/lang/Object
  #34 = NameAndType        #13:#14        // arr:[Ljava/lang/Object;
  #35 = Class              #39            // java/lang/System
  #36 = NameAndType        #40:#41        // out:Ljava/io/PrintStream;
  #37 = Class              #42            // java/io/PrintStream
  #38 = NameAndType        #43:#44        // println:(Ljava/lang/String;)V
  #39 = Utf8               java/lang/System
  #40 = Utf8               out
  #41 = Utf8               Ljava/io/PrintStream;
  #42 = Utf8               java/io/PrintStream
  #43 = Utf8               println
  #44 = Utf8               (Ljava/lang/String;)V
{
  public java.lang.String m1;
    descriptor: Ljava/lang/String;
    flags: ACC_PUBLIC

  public java.lang.String m2;
    descriptor: Ljava/lang/String;
    flags: ACC_PUBLIC

  public java.lang.Object[] arr;
    descriptor: [Ljava/lang/Object;
    flags: ACC_PUBLIC

  public com.company.jvm.Sample();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/company/jvm/Sample;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: new           #2                  // class com/company/jvm/Sample
         3: dup
         4: invokespecial #3                  // Method "<init>":()V
         7: astore_1
         8: aload_1
         9: ldc           #4                  // String 22
        11: putfield      #5                  // Field m1:Ljava/lang/String;
        14: aload_1
        15: bipush        12
        17: anewarray     #6                  // class java/lang/Object
        20: putfield      #7                  // Field arr:[Ljava/lang/Object;
        23: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
        26: aload_1
        27: getfield      #5                  // Field m1:Ljava/lang/String;
        30: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        33: return
      LineNumberTable:
        line 9: 0
        line 10: 8
        line 11: 14
        line 12: 23
        line 13: 33
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      34     0  args   [Ljava/lang/String;
            8      26     1 sample   Lcom/company/jvm/Sample;
}
SourceFile: "Sample.java"

1、访问标志

class文件中,我们可以通过背或记也好,或者通过查阅对照表。可以将常量池中的数据整理出来。常量池的数据,之后又是什么呢?紧接着的就是访问标志

标志名称 标志值 标志意义
ACC_PUBLIC 0X0001 是否为public类型
ACC_FINAL 0X0010 是否被声明为final,只有类可设置
ACC_SUPER 0x0020 在jdk1.0.2之后编译出来的类的这个标志都为真
ACC_INTERFACE 0x0200 标识是一个接口
ACC_ABSTRACT 0x4000 是否为abstract类型,如为真,其他类型均为假,如INTERFACE
ACC_SYNTHETIC 0x1000 标识这个类并非由用户产生
ACC_ANNOTATION 0x2000 标识这是一个注解
ACC_ENUM 0x4000 标识这是一个枚举
ACC_MODULE 0x8000 标识这是一个模块

这里先上一张二进制的图:
class文件中,常量池之后的相关数据解析!【class二进制文件分析】

下面是常量池之后的class文件截取的部分:

00 21 

访问标志,占用空间:U2,这里占用了十六进制的4个位,则是0x0021。这里我啰嗦一下,一个字节为8个位,对应到十六机制来说【两个位】代表一个字节。eg:0xF标识二进制的0000 1111,去除高位的0,就是1111。
在表格中我们说过,ACC_SUPER标志在jdk1.0.2之后的版本其值都为真,则是0x0020,说明其访问标志ACC_PUBLIC为真!0x0001|0x0020=0x0021

2、类索引、父类索引、接口索引

访问标志结束之后,就来到了我们所声明的类例,如下伪代码

public class dog extend cat implement animal{

}
00 02 00 06 00 00

__类索引、父类索引、接口索引占用的内存均为u2。

u2 索引 说明
00 02 #2 代表当前类的索引,通过查找为com/company/jvm/Sample
00 08 #8 代表当前父类索引,通过查找为Object
00 00 0 代表当前文件没有接口

字段表集合

通过字面意,就能得知这里将要介绍的是类或接口成员字段

//这里写个伪代码
public final static int AGE=10;

1、字段表结构

类型 名称 数量 说明
u2 filed_count 1 字段数量
u2 access_flag 1 访问标志
u2 name_index 1 名称索引
u2 descriptor_index 1 类型索引
u2 attribute_count 1 属性计数器
u2 attributes attribute_count 属性值集合

2、字段表的访问标识

标志名称 标志值 含义
ACC_PUBLIC 0x0001 字段是否为公开
ACC_PRIVATE 0x0002 字段是否为私有
ACC_PROTECTED 0x0004 字段是否为保护
ACC_STATIC 0x0008 字段是否为静态
ACC_FINAL 0x0010 字段是否为Final
ACC_VOLATILE 0x0040 字段是否在并发时可见
ACC_TRANSIENT 0x0080 字段是否序列化
ACC_SYNTHETIC 0x1000 字段是否由编译器自己决定
ACC_ENUM 0x4000 字段是否为枚举
00 03 00 01 00 0a 00 0b  00 00 00 01 00 0c 00 0b 00 00 00 01 00 0d 00 0e 00 00 
位值 说明
第一个成员字段
00 03 代表字段数量有3个
00 01 代表字段访问标志位public
00 0a 代表名称索引为10 名称为 m1
00 0b 代表descriptor的索引值为11 对象类型为Ljava/lang/String;【分号;全限定名】
00 00 代表没有属性数量
第二个成员字段
00 01 代表字段访问标志位为public
00 0c 代表字段名称索引为12,名称为m2
00 0b 代表类型的索引11,类型为 Ljava/lang/String;同上
00 00 代表没有属性
第三个成员字段
00 01 同上
00 0d 名称索引为13,经查找为arr
00 0e 类型索引为14,经查找为 [Ljava/lang/Object;全限定名,其中“[”代表为数组
00 00 代表没有属性,数量为0

方法表集合

唠嗑时间开始,写到这里花了三个多小时。从排版到书写上面确实有很大的提升。此时的我确实有点疲惫。仔细一想,没啥子疲惫不疲惫的,路虽远,但始终在路上,总会到达终点。突然想到书上说过这样的一句:当你在解决一个问题的时候,你会感到很疲惫,这时候千万别放弃。因为大部分的人就此放弃了,而你还在路上行走。当你解决之后,你又比别人强了不少!

写到这里【字段表集合】之后,这里就会很轻松。这里再啰嗦一下,字段表分为:成员字段数量、字段名索引、字段类型索引、字段属性数量、字段属性集合。

1、方法表结构

类型 名称 数量 说明
method_count 方法数量
u2 access_flag 1 访问标志
u2 name_index 1 名称索引
u2 descriptor_index 1 类型索引
u2 attributes_count 1 属性数量
attributes_count attributes_[attributes_count] 1 属性表

2、方法表的访问标识

标志名称 标志值 含义
ACC_PUBLIC 0x0001 方法是否为公开
ACC_PRIVATE 0x0002 方法是否为私有
ACC_PROTECTED 0x0004 方法是否为保护
ACC_STATIC 0x0008 方法是否为静态
ACC_FINAL 0x0010 方法是否为Final
ACC_SYNCHRONIZED 0x0020 方法是否在并发时可见
ACC_BRIDGE 0x0040 方法是不是由编译器产生的桥接方法
ACC_VARCHAR 0x0080 方法是否接收不可定参数
ACC_NATIVE 0x0100 方法是否为Native
ACC_ABSTRACT 0x0400 方法是否为abstract
ACC_STRICT 0x0800 方法是否为strictfp【修饰在接口和类,对精确率类型较高且跨平台的计算结果要求比较严格的清醒的话,建议使用该strictfp关键词。】
ACC_SYNTHETIC 0x1000 方法是否由编译器自动产生

code属性表

类型 名称 数量
u2 attribute_name_index 1
u4 attribute_length 1
u2 max_stack 1
u2 max_locals 1
u4 code_length 1
u1 code code_length
u2 exception_table_length 1
exception_info exception_table exception_table_length
u2 attributes_count 1
attribute_info attributes attributes_count

继续上方法表的字节码:

00 02 00 01 00 0f 00 10 00 01 00 11 00 00 00 2f 00 00 00 01 00 00 00 05 2a b7 00 01 b1 00 00

现在对照上面的说明,现在逐一说明:

对照 位码 说明
methods_count 00 02 存在2个方法
acc_flags 00 01 访问标识 public
name_index 00 0f 方法名索引为15,经查为< init >
descriptor_index 00 10 方法描述索引为16,经常为()v
attributes_count 00 01 属性只有一个
attributes_name_index 00 11 属性名索引为17,经查为code
attributes_length 00 00 00 2f 属性值长度为47
max_stack 00 00 最大栈深为0
max_locals 00 01 需要分配1个变量槽,根据同时生存的最大局部变量数和类型计算
code_length 00 00 00 05 字节码长度
code 2a 查看指令表为aload_0
code b7 同上~,invokespecial,调用超类构造方法,实例初始化方法,私有方法
code 00 同上~,nop,什么都不做
code 01 同上~,acoust_null,将null推送至栈顶
code b1 同上~,return ,从当前方法返回void
exception_table_lenght 00 00 当前没有发现异常信息
attributes_count 00 02 该方法的附加属性共有2个
attribute_name_index 0012 属性名索引为18,经查为 LineNumberTable
attribute_length 00 00 00 06 属性长度为6
line_number_table_length 00 01 字节码行号共1行
star_pc 00 00 从字节码第0行开始。此处说的行数是一种抽象的,指的是相对于方法体的偏移
line_number 00 03java行号为3
attribute_name_index 00 13 属性名称索引为19,经查为 LocalVariableTable
attribute_length 00 00 00 0c 属性长度为12
local_variable_table_length 00 01 局部变量表长度为1
star_pc 00 00 局部变量的生命周期开始的字节码偏移量
length 00 05 往后偏移5个地址的长度,star_pc和length的配合使用就是局部变量在字节码中的作用域范围
name_index 00 14 名字索引为20,经查为 this
descriptor_index 00 15 描述索引为21,经查为Lcom/company/jvm/Sample;
index 00 00 这个局部变量在栈帧的局部变量表中变量槽的为之为0
acc_flag 00 09 public的标志0x0001,static的标志0x0008,0x0001
name_index 00 16 名字为索引22,经查为 main
descriptor_index 00 17 描述的索引为23,经查([Ljava/lang/String;)V
attributes_count 00 01 当前方法的属性长度为1
attribute_name_index 00 11 当前属性名称索引值为17,经查为Code
attribute_length 00 00 00 66 当前属性的长度为102
attribute_name_index 00 02 属性名称索引为2,经查为com/company/jvm/Sample
max_stack 00 02 栈帧最大深度为2
max_local 00 02 最大局部变量槽数为2
code_lenght 00 00 00 22 字节码长度为34
code bb 经查字节码指令,0xbb为new
code 00 Nop,什么事都不做
code 02 同上经查,为将int型-1推送至栈顶
code 59 0x59,经查为dump,赋值栈顶数值并压入栈顶
code b7 0xb7,invokespecial,调用超类构造方法,实例化初始方法,私有方法
code 00 Nop 不做任何事
code 03 0x03,将int的0推送至栈顶
code 0x27e 一直都这个位都是code值
exception_table_lenght 00 00 说明没有任何异常信息
attributes_count 00 02 说明有两条
同上面分析~ 同上面分析~ 同上面分析~

到最后8位为Source

标识 位码 说明
attribute_name_index 00 1b 名称索引为27,经查为SourceFile
attribute_length 00 00 00 02 对应值为2
sourcefile_index 00 1c 对应的文件索引值为28, 经查为Sample.java

至此一个简单的文件就翻译完成了,对照着javap和字节码整理之后,确实有一番收获。但是还处于道可道非常道的过程,仍然需要透彻一些!明天继续干~文章来源地址https://www.toymoban.com/news/detail-469025.html

到了这里,关于class文件中,常量池之后的相关数据解析!【class二进制文件分析】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C#蓝牙连接及传输数据的三种方式(蓝牙传输文件、二进制数据)

          先下载InTheHand.Net.Personal.dll并在C#中引用,这个需要在网上下载      先看界面            这种方式优点是稳定性较强,基本无错误,就是偶尔需要提前蓝牙配对。        这种方式直接与蓝牙设备进行配对的时候会报错,请求的地址无效,这时候需要在被检测的蓝牙

    2024年02月11日
    浏览(60)
  • 深入理解前端字节二进制知识以及相关API

    当前,前端对二进制数据有许多的API可以使用,这丰富了前端对文件数据的处理能力,有了这些能力,就能够对图片等文件的数据进行各种处理。 本文将着重介绍一些前端二进制数据处理相关的API知识,如Blob、File、FileReader、ArrayBuffer、TypeArray、DataView等等。 在介绍各种API之

    2024年02月03日
    浏览(29)
  • 在线解析二进制报文

    智能设备应用越来越普遍,深入到生活的各个方面,从智慧农业到智能制造,从水利灌溉到电力传输,从工业生产到智能家居。智能设备应用在各个领域,设备之间都是通过数据交换来达到信息共享和互相操作,交换的数据都遵守某个协议标准,在测试时,调试时和排查问题

    2024年02月06日
    浏览(29)
  • 【Unity】二进制文件 数据持久化(修改版)【个人复习笔记/有不足之处欢迎斧正/侵删】

             变量的本质都是二进制 ,在内存中都以字节的形式存储着,通过sizeof方法可以看到常用变量类型占用的字节空间长度( 1byte = 8bit,1bit(位)不是0就是1 )         二进制文件读写的本质: 将各类型变量转换为字节数组,将字节数组直接存储到文件中 ,不仅可以节

    2024年04月17日
    浏览(36)
  • uniapp小程序(原生微信小程序也可以使用),获取接口二进制流数据上传文件到服务器

    需求:通过接口返回的二进制流数据,这个流数据他是一个xlsx文档,需要给到用户一个文档线上连接。 下面是具体代码,注意只针对二进制的文件数据,如果图片上传直接调用uploadFile就可以,并且兼容原生微信小程序。  

    2024年02月16日
    浏览(30)
  • 深入解析位运算算法:探索数字的二进制秘密

    位运算是计算机科学中的重要概念,用于在二进制数字层面进行各种操作。本文将深入介绍位运算的基本操作,以及它在判断、计算和处理数字中的应用,包括判断2的幂次方、位图法、位掩码和寻找缺失数字等。 位操作是通过对数字的二进制表示进行操作,实现各种功能。

    2024年02月11日
    浏览(31)
  • 算法-回溯相关问题-生成所有n位长的二进制字符串 Java版

    生成所有n位长的二进制字符串。假设A[0…n-1]是一个大小为n的数组。

    2024年02月16日
    浏览(27)
  • Linux查看二进制文件

    hexdump 、 hd 、 od 、 xxd hexdump 、 hd 可以使用16进制、10进制、8进制、 ascii 码的形式查看文件。 执行 就会看到hd其实只是hexdump的一个软链接。 使用 man hexdump ,可以查看 hexdump 的各种参数。 length and offset 参数后面可以跟后缀KiB(=1024)、MiB(=1024 1024),依此类推GiB、TiB、PiB、

    2024年02月08日
    浏览(35)
  • Python读写二进制文件

    Python 读写文件的二进制数据需要使用到struct模块,进行C/C++与Python数据格式的转换。 struct模块中最常用的函数为pack和unpack,用法如下: 函数 return explain pack(fmt,v1,v2…) string 按照给定的格式(fmt),把数据转换成字符串(字节流),并将该字符串返回. pack_into(fmt,buffer,offset,v1,v2…) No

    2024年02月08日
    浏览(33)
  • 【VSCode】查看二进制文件

    1.安装插件Hex Editor 2.打开二进制文件 3.执行Hex Editor命令

    2024年02月13日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包