Java编译后的class文件解析

本文详细解析了Java编译后的.class文件结构,包括魔数、文件版本号、常量池、访问标志、类索引、父类索引、接口计数器、字段表、方法表以及属性表等内容,深入探讨了每个部分的作用和数据结构,帮助读者理解Java字节码的本质。
摘要由CSDN通过智能技术生成

文章已同步github博客:Java编译后的class文件解析

1、编译Java类

1.1、写Java类

编写一份Java类,即.java文件,例如:

package com.jesus.util;

public class TestDemo {

    public static final String SUCCESS = "success";

    public static void main(String[] args) {
        System.out.println("main");
    }

    private String printText(String str) {
        System.out.println(str);
        return SUCCESS;
    }
}

1.2、编译

通过javac命令编译为class文件:

javac DemoTest.java

2、.class文件结构

生成的.class文件如下:

cafe babe 0000 0034 0023 0a00 0700 1509
0016 0017 0800 0f0a 0018 0019 0700 1a08
001b 0700 1c01 0007 5355 4343 4553 5301
0012 4c6a 6176 612f 6c61 6e67 2f53 7472
696e 673b 0100 0d43 6f6e 7374 616e 7456
616c 7565 0100 063c 696e 6974 3e01 0003
2829 5601 0004 436f 6465 0100 0f4c 696e
654e 756d 6265 7254 6162 6c65 0100 046d
6169 6e01 0016 285b 4c6a 6176 612f 6c61
6e67 2f53 7472 696e 673b 2956 0100 0970
7269 6e74 5465 7874 0100 2628 4c6a 6176
612f 6c61 6e67 2f53 7472 696e 673b 294c
6a61 7661 2f6c 616e 672f 5374 7269 6e67
3b01 000a 536f 7572 6365 4669 6c65 0100
0d54 6573 7444 656d 6f2e 6a61 7661 0c00
0b00 0c07 001d 0c00 1e00 1f07 0020 0c00
2100 2201 0017 636f 6d2f 6a65 7375 732f
7574 696c 2f54 6573 7444 656d 6f01 0007
7375 6363 6573 7301 0010 6a61 7661 2f6c
616e 672f 4f62 6a65 6374 0100 106a 6176
612f 6c61 6e67 2f53 7973 7465 6d01 0003
6f75 7401 0015 4c6a 6176 612f 696f 2f50
7269 6e74 5374 7265 616d 3b01 0013 6a61
7661 2f69 6f2f 5072 696e 7453 7472 6561
6d01 0007 7072 696e 746c 6e01 0015 284c
6a61 7661 2f6c 616e 672f 5374 7269 6e67
3b29 5600 2100 0500 0700 0000 0100 1900
0800 0900 0100 0a00 0000 0200 0600 0300
0100 0b00 0c00 0100 0d00 0000 1d00 0100
0100 0000 052a b700 01b1 0000 0001 000e
0000 0006 0001 0000 0003 0009 000f 0010
0001 000d 0000 0025 0002 0001 0000 0009
b200 0212 03b6 0004 b100 0000 0100 0e00
0000 0a00 0200 0000 0800 0800 0900 0200
1100 1200 0100 0d00 0000 2600 0200 0200
0000 0ab2 0002 2bb6 0004 1206 b000 0000
0100 0e00 0000 0a00 0200 0000 0c00 0700
0d00 0100 1300 0000 0200 14

2.1、.class文件组成

以上可知,.class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在.class文件之中,中间没有添加任何分隔符;

根据Java虚拟机规范的规定,.class文件格式采用一种类似于C语言的伪结构来存储数据,包含无符号数和表:

  • 无符号数:以u1、u2、u4、u8来分别代表1个字节、2个字节、4个字节和8个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照UTF-8编码构成的字符串值;
  • 表:由多个无符号数或者其他表作为数据项构成的复合数据类型,所有表都习惯性的以”_info“结尾。

Class文件的结构没有分隔符,无论你是数量还是顺序,都是严格规定的,哪个字节代表什么含义、长度多少、先后顺序如何,都不允许改变。

2.2、.class文件结构

ClassFile {
  u4             magic; // 魔数,固定值 0xCAFEBABE
  u2             minor_version; // 副版本号
  u2             major_version; // 主版本号
  u2             constant_pool_count; // 常量池计数器
  cp_info        constant_pool[constant_pool_count-1]; // 常量池
  u2             access_flags; // 访问标志
  u2             this_class; // 类索引
  u2             super_class; // 父类索引
  u2             interfaces_count; // 接口计数器
  u2             interfaces[interfaces_count]; // 接口表
  u2             fields_count; // 字段计数器
  field_info     fields[fields_count]; // 字段表
  u2             methods_count; // 方法计数器
  method_info    methods[methods_count]; // 方法表
  u2             attributes_count; // 属性计数器
  attribute_info attributes[attributes_count]; // 属性表
}
2.2.1、魔数

魔数(Magic Number),u4,即class文件中用4个字节表示,他的唯一作用就是确定这个文件是否为一个能否被虚拟机所识别的class文件,魔数的固定值为0xCAFEBABE,不会改变;

2.2.2、文件版本号

紧挨着魔数的2个字节为副版本号minor_version(上面class文件例子中0x0000),接下来的2个字节为主版本号major_version(上面例子中的0x0034,换算成10进制为52,查询jdk版本对应关系,主版本52为jdk 1.8);

2.2.3、常量池计数器

接下来的2个字节表示常量池计数器constant_pool_count,常量池中的数量不固定,constant_pool_count的值 = 常量池中的成员数 + 1,常量池的索引从1开始;

例如上面class文件中的16进制数为0x0023,换算成10进制为35,通过命令“javap -v DemoTest.class"来查看常量池,上面class文件的常量池内容如下:

xxxMacBook-Pro:JVM_test xxx$ javap -v TestDemo.class 
Classfile /Users/xxx/xxx/TestDemo.class
  Last modified 2019-12-4; size 603 bytes
  MD5 checksum 47bd6066637c077873b14b73e810b1e2
  Compiled from "TestDemo.java"
public class com.jesus.util.TestDemo
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #7.#21         // java/lang/Object."<init>":()V
   #2 = Fieldref           #22.#23        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #15            // main
   #4 = Methodref          #24.#25        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #26            // com/jesus/util/TestDemo
   #6 = String             #27            // success
   #7 = Class              #28            // java/lang/Object
   #8 = Utf8               SUCCESS
   #9 = Utf8               Ljava/lang/String;
  #10 = Utf8               ConstantValue
  #11 = Utf8               <init>
  #12 = Utf8               ()V
  #13 = Utf8               Code
  #14 = Utf8               LineNumberTable
  #15 = Utf8               main
  #16 = Utf8               ([Ljava/
  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

若邪〃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值