Class文件如何存储基本数据,引用类型

本文概述:
在上一个文章我们谈论了关于字节码的信息.
这里我们来看看 Class 文件是什么

Class 文件是什么

还是老样子, 不直接说是啥, 应该先了解为什么需要.

其实你看这个名字就了解到了, 肯定和类有关系. 我们思考一下, 我们学习的时候肯定都有了解过, Java 是跨平台的, 那么如何跨平台呢?

答案就是这个 Class 文件, 名字很明显, 保存的就是一个类的信息.

也就是说, 这个 Class 文件保存了一个类的所有信息, Class 文件是可以开平台的 (注: 拓展思考一下, 你可能了解过 JVM 里面也有 Class 类信息, 事实上,Class 类信息就是根据这个 Class 文件而产生的, 所以说不同平台, 虚拟机不同, 只要虚拟机是按照规范设计的, 就可以加载这个 Class 文件.)

Class 文件里面存储了类的所有信息, 其实到这里, 一定要做的是,Class 文件里面 <都需要存储哪些东西>,如果你知道, 也不要去回忆他存储哪些东西, <最重要的是,一个Class里面有哪些信息>

由问题来思考需要保存哪些信息:

  1. 版本问题: 1.8 和 1.7 的 Class 文件能通用吗?, 肯定不行, 那么就需要保存版本号
  2. 下面分析就简单多了, 我们编写类的时候有啥
    1. 类信息: 类全限定名, 修饰符, 如果实现了父类, 是不是还需要父类的全限定名和修饰符. 或者说实现了接口, 接口的全限定名, 修饰符等
    2. 方法信息: 一个类中有很多方法吧, 方法名, 修饰符, 返回值, 参数.
    3. 属性信息: 比如说基本数据类型, 引用对象等等.

有个很重要的就是: Class 常量池, 这货在类加载器加载了 Class 文件之后就会变成 Class 运行时常量池, 等会会介绍一下.

下面来主要来介绍一下 Class 文件存储属性, 因为我觉得如何保存属性更好玩, 保存类信息, 基本上都是保存字符串啥的, 没有什么很好玩的地方.

Class 文件如何存储属性

Class 文件中一个结构,Constant Pool, 我们可以叫他 Class 静态常量池

在这里插入图片描述

Tag 值是啥啊, 到底是如何存储的, 图片更直观

常量池里面都存储哪些信息呢?

Classfile /E:/VSCODE_FILE/Test/src/main/java/org/example/jvm/ConstantPoolTest.class
  Last modified 2023-3-14; size 693 bytes                                                
  MD5 checksum ac8bce83662ec1afa8a0ae98d8ad91c9                                          
  Compiled from "ConstantPoolTest.java"                                                  
public class org.example.jvm.ConstantPoolTest                                            
  minor version: 0                                                                       
  major version: 52                                                                      
  flags: ACC_PUBLIC, ACC_SUPER                                                           
Constant pool:                // 这里是静态常量池哦!!!                                                           
   #1 = Methodref          #16.#42        // java/lang/Object."<init>":()V               
   #2 = Fieldref           #15.#43        // org/example/jvm/ConstantPoolTest.int_num:I  
   #3 = Fieldref           #15.#44        // org/example/jvm/ConstantPoolTest.char_num:C 
   #4 = Fieldref           #15.#45        // org/example/jvm/ConstantPoolTest.short_num:S
   #5 = Float              130.0f                                                        
   #6 = Fieldref           #15.#46        // org/example/jvm/ConstantPoolTest.float_num:F
   #7 = Double             140.0d
   #9 = Fieldref           #15.#47        // org/example/jvm/ConstantPoolTest.double_num:D
  #10 = Fieldref           #15.#48        // org/example/jvm/ConstantPoolTest.byte_num:B
  #11 = Long               3333l
  #13 = Fieldref           #15.#49        // org/example/jvm/ConstantPoolTest.long_num:J
  #14 = Fieldref           #15.#50        // org/example/jvm/ConstantPoolTest.boolean_flag:Z
  #15 = Class              #51            // org/example/jvm/ConstantPoolTest
  #16 = Class              #52            // java/lang/Object
  #17 = Utf8               int_num
  #18 = Utf8               I
  #19 = Utf8               char_num
  #20 = Utf8               C
  #21 = Utf8               short_num
  #22 = Utf8               S
  #23 = Utf8               float_num
  #24 = Utf8               F
  #25 = Utf8               double_num
  #26 = Utf8               D
  #27 = Utf8               byte_num
  #28 = Utf8               B
  #29 = Utf8               long_num
  #30 = Utf8               J
  #31 = Utf8               long_delay_num
  #32 = Utf8               boolean_flag
  #33 = Utf8               Z
  #34 = Utf8               <init>
  #35 = Utf8               ()V
  #36 = Utf8               Code
  #37 = Utf8               LineNumberTable
  #38 = Utf8               main
  #39 = Utf8               ([Ljava/lang/String;)V
  #40 = Utf8               SourceFile
  #41 = Utf8               ConstantPoolTest.java
  #42 = NameAndType        #34:#35        // "<init>":()V
  #43 = NameAndType        #17:#18        // int_num:I
  #44 = NameAndType        #19:#20        // char_num:C
  #45 = NameAndType        #21:#22        // short_num:S
  #46 = NameAndType        #23:#24        // float_num:F
  #47 = NameAndType        #25:#26        // double_num:D
  #48 = NameAndType        #27:#28        // byte_num:B
  #49 = NameAndType        #29:#30        // long_num:J
  #50 = NameAndType        #32:#33        // boolean_flag:Z
  #51 = Utf8               org/example/jvm/ConstantPoolTest
  #52 = Utf8               java/lang/Object
{
  public org.example.jvm.ConstantPoolTest();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: bipush        110
         7: putfield      #2                  // Field int_num:I
        10: aload_0
        11: bipush        97
        13: putfield      #3                  // Field char_num:C
        16: aload_0
        17: bipush        120
        19: putfield      #4                  // Field short_num:S
        22: aload_0
        23: ldc           #5                  // float 130.0f
        25: putfield      #6                  // Field float_num:F
        28: aload_0
        29: ldc2_w        #7                  // double 140.0d
        32: putfield      #9                  // Field double_num:D
        35: aload_0
        36: bipush        111
        38: putfield      #10                 // Field byte_num:B
        41: aload_0
        42: ldc2_w        #11                 // long 3333l
        45: putfield      #13                 // Field long_num:J
        48: aload_0
        49: iconst_1
      LineNumberTable:
        line 13: 0
        line 15: 4
        line 16: 10
        line 17: 16
        line 18: 22
        line 19: 28
        line 20: 35
        line 21: 41
        line 23: 48

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 27: 0
}
SourceFile: "ConstantPoolTest.java"
PS E:\VSCODE_FILE\Test\src\main\java\org\example\jvm> javac .\ConstantPoolTest.java          
PS E:\VSCODE_FILE\Test\src\main\java\org\example\jvm> javap -verbose .\ConstantPoolTest.class
Classfile /E:/VSCODE_FILE/Test/src/main/java/org/example/jvm/ConstantPoolTest.class
  Last modified 2023-3-14; size 710 bytes
  MD5 checksum 30896b5e6fccdb34ecc7c03e41cfbed5
  Compiled from "ConstantPoolTest.java"
public class org.example.jvm.ConstantPoolTest
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #17.#43        // java/lang/Object."<init>":()V
   #2 = Fieldref           #16.#44        // org/example/jvm/ConstantPoolTest.int_num:I
   #3 = Fieldref           #16.#45        // org/example/jvm/ConstantPoolTest.char_num:C
   #4 = Fieldref           #16.#46        // org/example/jvm/ConstantPoolTest.short_num:S
   #5 = Float              130.0f
   #6 = Fieldref           #16.#47        // org/example/jvm/ConstantPoolTest.float_num:F
   #7 = Double             140.0d
   #9 = Fieldref           #16.#48        // org/example/jvm/ConstantPoolTest.double_num:D
  #10 = Fieldref           #16.#49        // org/example/jvm/ConstantPoolTest.byte_num:B
  #11 = Long               3333l
  #13 = Fieldref           #16.#50        // org/example/jvm/ConstantPoolTest.long_num:J
  #14 = Fieldref           #16.#51        // org/example/jvm/ConstantPoolTest.boolean_flag:Z
  #15 = String             #52            // ggzx !!!!,这里是我定义的字符串
  #16 = Class              #53            // org/example/jvm/ConstantPoolTest
  #17 = Class              #54            // java/lang/Object
  #18 = Utf8               int_num
  #19 = Utf8               I
  #20 = Utf8               char_num
  #21 = Utf8               C
  #22 = Utf8               short_num
  #23 = Utf8               S
  #24 = Utf8               float_num
  #25 = Utf8               F
  #26 = Utf8               double_num
  #27 = Utf8               D
  #28 = Utf8               byte_num
  #29 = Utf8               B
  #30 = Utf8               long_num
  #31 = Utf8               J
  #32 = Utf8               long_delay_num
  #33 = Utf8               boolean_flag
  #34 = Utf8               Z
  #35 = Utf8               <init>
  #36 = Utf8               ()V
  #37 = Utf8               Code
  #38 = Utf8               LineNumberTable
  #39 = Utf8               main
  #40 = Utf8               ([Ljava/lang/String;)V
  #41 = Utf8               SourceFile
  #42 = Utf8               ConstantPoolTest.java
  #43 = NameAndType        #35:#36        // "<init>":()V
  #44 = NameAndType        #18:#19        // int_num:I
  #45 = NameAndType        #20:#21        // char_num:C
  #46 = NameAndType        #22:#23        // short_num:S
  #47 = NameAndType        #24:#25        // float_num:F
  #48 = NameAndType        #26:#27        // double_num:D
  #49 = NameAndType        #28:#29        // byte_num:B
  #50 = NameAndType        #30:#31        // long_num:J
  #51 = NameAndType        #33:#34        // boolean_flag:Z
  #52 = Utf8               ggzx
  #53 = Utf8               org/example/jvm/ConstantPoolTest
  #54 = Utf8               java/lang/Object
{
  public org.example.jvm.ConstantPoolTest();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: bipush        110
         7: putfield      #2                  // Field int_num:I
        10: aload_0
        11: bipush        97
        13: putfield      #3                  // Field char_num:C
        16: aload_0
        17: bipush        120
        19: putfield      #4                  // Field short_num:S
        22: aload_0
        23: ldc           #5                  // float 130.0f
        25: putfield      #6                  // Field float_num:F
        28: aload_0
        29: ldc2_w        #7                  // double 140.0d
        32: putfield      #9                  // Field double_num:D
        35: aload_0
        36: bipush        111
        38: putfield      #10                 // Field byte_num:B
        41: aload_0
        42: ldc2_w        #11                 // long 3333l
        45: putfield      #13                 // Field long_num:J
        48: aload_0
        49: iconst_1
        50: putfield      #14                 // Field boolean_flag:Z
        53: return
      LineNumberTable:
        line 13: 0
        line 15: 4
        line 16: 10
        line 17: 16
        line 18: 22
        line 19: 28
        line 20: 35
        line 21: 41
        line 23: 48

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=2, args_size=1
         0: ldc           #15                 // String ggzx
         2: astore_1
         3: return
      LineNumberTable:
        line 26: 0
        line 27: 3
}
SourceFile: "ConstantPoolTest.java"

结论:

  1. final 修饰的 8 种基本类型的值, 会全部进入常量池
  2. 非 final 修饰的, 也包括使用了 static 的情况, 只有 double, float,long 的值会进入常量池
  3. 所有字符串类型字面量 这里在以后的文章中的 StringTable 会讲, 很有趣
先看看如何存储 int 和 float 数据类型的变量

![[Pasted image 20230314134952.png]]

我们只需要了解, 对于这类 int, float,long, double 基本数据类型, 每个数据肯定都需要相同的存储空间.

String 又如何存储呢?

刚才上面我们说到, 对于哪一类固定存储空间的, 很简单. 那么 String 呢?String 需要的空间不一定啊.

这里我们要搞清楚一个点, 就是 String 和字符串在这里的小小区别 ,就是 String 并不是基本数据类型, 字符串是一个常量,String 属于引用, 其引用指向的内容, 是字符串.这里很重要嗷

既然谈到字符串了, 先来看看字符串如何存储的

// 下面的ConstantPool里面的部分 
#15 = String             #52            // ggzx !!!!,这里是我定义的字符串
#52 = Utf8               ggzx

// 这里是main方法的字节码
Code:
  stack=1, locals=2, args_size=1
	 0: ldc           #15                 // String ggzx
	 2: astore_1
	 3: return

欸, 怎么"定义了"两个 "ggzx"字符串, 但是其实没有哦, 这里的 #15 是啥呢, 是 String 引用, 那后面的 #52 有是啥, 是一个 Utf-8 类型欸, 这里指的就是一个字符串内容.

也就是说,String 是一个引用, 他保存着一个字符串, 所有字符串都是有编号的, String 直接指向字符串就可以了.

现在我们再来看看是 String 如何存储的,
![[Pasted image 20230314135408.png]]

看看这里,String 是一个引用, 他只需要保存目标字符串的序号即可.

在看一个图片:
![[Pasted image 20230314141501.png]]

引用类型如何存储的

经过 String 的分析, 你可能有一些头绪了. 引用类型, 是也给引用指向在堆中分配的实例数据.Class 就是一个文本哇, 哪里有堆?
来看看引用类型的结构:
![[Pasted image 20230314141529.png]]

这个是不是和 String 很像, 保存的依然是序号.
那这个序号存储的是啥呢?

符号引用: 不知道你看见这个的时候, 有没有想到类加载阶段的解析阶段. 对的, 没错, 就是哪个.
符号引用是啥呢:
符号引用本质上就是字符串, 但是这个字符串是具有意义的, 根据这个字符串能找到目标.
比如说类的全限定名: com.ggzx.User, 当然接口也可以.
在类加载的解析阶段: 会把符号引用解析成直接引用
解析动作主要针对于接口字段类方法接口方法,方法类型,因为没有将符号引用替换之前,比如说一个类,一个类的字段,调用的方法,其都是一个字符,而将符号引用解析成直接引用的过程是找到这个方法,这个类,这个属性的一个存储位置的指针。

撰文不易, 期望一个小小的赞.
本文讲的确实不多, 是因为我自己比较喜欢记录自己思考过的东西, 而不是去写一篇"说明书".
下一篇文章仍然会讲述 Class 文件相关的知识

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

渣渣高不会写Java

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

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

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

打赏作者

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

抵扣说明:

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

余额充值