本文概述:
在上一个文章我们谈论了关于字节码的信息.
这里我们来看看 Class 文件是什么
还是老样子, 不直接说是啥, 应该先了解为什么需要.
其实你看这个名字就了解到了, 肯定和类有关系. 我们思考一下, 我们学习的时候肯定都有了解过, Java 是跨平台的, 那么如何跨平台呢?
答案就是这个 Class 文件, 名字很明显, 保存的就是一个类的信息.
也就是说, 这个 Class 文件保存了一个类的所有信息, Class 文件是可以开平台的 (注: 拓展思考一下, 你可能了解过 JVM 里面也有 Class 类信息, 事实上,Class 类信息就是根据这个 Class 文件而产生的, 所以说不同平台, 虚拟机不同, 只要虚拟机是按照规范设计的, 就可以加载这个 Class 文件.)
Class 文件里面存储了类的所有信息, 其实到这里, 一定要做的是,Class 文件里面 <都需要存储哪些东西>,如果你知道, 也不要去回忆他存储哪些东西, <最重要的是,一个Class里面有哪些信息>
由问题来思考需要保存哪些信息:
有个很重要的就是: Class 常量池, 这货在类加载器加载了 Class 文件之后就会变成 Class 运行时常量池, 等会会介绍一下.
下面来主要来介绍一下 Class 文件存储属性, 因为
Class 文件中一个结构,Constant Pool, 我们可以叫他 Class 静态常量池
Tag 值是啥啊, 到底是如何存储的, 图片更直观
常量池里面都存储哪些信息呢?
Classfile /E:/VSCODE_FILE/Test/src/main/java/org/example/jvm/ConstantPoolTest.classLast 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."":()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 #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 // "":()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: ()Vflags: ACC_PUBLICCode:stack=3, locals=1, args_size=10: aload_01: invokespecial #1 // Method java/lang/Object."":()V4: aload_05: bipush 1107: putfield #2 // Field int_num:I10: aload_011: bipush 9713: putfield #3 // Field char_num:C16: aload_017: bipush 12019: putfield #4 // Field short_num:S22: aload_023: ldc #5 // float 130.0f25: putfield #6 // Field float_num:F28: aload_029: ldc2_w #7 // double 140.0d32: putfield #9 // Field double_num:D35: aload_036: bipush 11138: putfield #10 // Field byte_num:B41: aload_042: ldc2_w #11 // long 3333l45: putfield #13 // Field long_num:J48: aload_049: iconst_1LineNumberTable:line 13: 0line 15: 4line 16: 10line 17: 16line 18: 22line 19: 28line 20: 35line 21: 41line 23: 48public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=0, locals=1, args_size=10: returnLineNumberTable: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.classLast modified 2023-3-14; size 710 bytesMD5 checksum 30896b5e6fccdb34ecc7c03e41cfbed5Compiled from "ConstantPoolTest.java"
public class org.example.jvm.ConstantPoolTestminor version: 0major version: 52flags: ACC_PUBLIC, ACC_SUPER
Constant pool:#1 = Methodref #17.#43 // java/lang/Object."":()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 #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 // "":()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: ()Vflags: ACC_PUBLICCode:stack=3, locals=1, args_size=10: aload_01: invokespecial #1 // Method java/lang/Object."":()V4: aload_05: bipush 1107: putfield #2 // Field int_num:I10: aload_011: bipush 9713: putfield #3 // Field char_num:C16: aload_017: bipush 12019: putfield #4 // Field short_num:S22: aload_023: ldc #5 // float 130.0f25: putfield #6 // Field float_num:F28: aload_029: ldc2_w #7 // double 140.0d32: putfield #9 // Field double_num:D35: aload_036: bipush 11138: putfield #10 // Field byte_num:B41: aload_042: ldc2_w #11 // long 3333l45: putfield #13 // Field long_num:J48: aload_049: iconst_150: putfield #14 // Field boolean_flag:Z53: returnLineNumberTable:line 13: 0line 15: 4line 16: 10line 17: 16line 18: 22line 19: 28line 20: 35line 21: 41line 23: 48public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=1, locals=2, args_size=10: ldc #15 // String ggzx2: astore_13: returnLineNumberTable:line 26: 0line 27: 3
}
SourceFile: "ConstantPoolTest.java"
结论:
我们只需要了解, 对于这类 int, float,long, double 基本数据类型, 每个数据肯定都需要相同的存储空间.
刚才上面我们说到, 对于哪一类固定存储空间的, 很简单. 那么 String 呢?String 需要的空间不一定啊.
这里我们要搞清楚一个点, 就是 String 和字符串在这里的小小区别 ,就是 String 并不是基本数据类型, 字符串是一个常量,String 属于引用, 其引用指向的内容, 是字符串.这里很重要嗷
既然谈到字符串了, 先来看看字符串如何存储的
// 下面的ConstantPool里面的部分
#15 = String #52 // ggzx !!!!,这里是我定义的字符串
#52 = Utf8 ggzx// 这里是main方法的字节码
Code:stack=1, locals=2, args_size=10: ldc #15 // String ggzx2: astore_13: return
欸, 怎么"定义了"两个 "ggzx"字符串, 但是其实没有哦, 这里的 #15 是啥呢, 是 String 引用, 那后面的 #52 有是啥, 是一个 Utf-8 类型欸, 这里指的就是一个字符串内容.
也就是说,String 是一个引用, 他保存着一个字符串, 所有字符串都是有编号的, String 直接指向字符串就可以了.
现在我们再来看看是 String 如何存储的,
看看这里,String 是一个引用, 他只需要保存目标字符串的序号即可.
在看一个图片:
经过 String 的分析, 你可能有一些头绪了. 引用类型, 是也给引用指向在堆中分配的实例数据.Class 就是一个文本哇, 哪里有堆?
来看看引用类型的结构:
这个是不是和 String 很像, 保存的依然是序号.
那这个序号存储的是啥呢?
符号引用: 不知道你看见这个的时候, 有没有想到类加载阶段的解析阶段. 对的, 没错, 就是哪个.
符号引用是啥呢:
符号引用本质上就是字符串, 但是这个字符串是具有意义的, 根据这个字符串能找到目标.
比如说类的全限定名: com.ggzx.User, 当然接口也可以.
在类加载的解析阶段: 会把符号引用解析成直接引用
解析动作主要针对于接口字段类方法接口方法,方法类型,因为没有将符号引用替换之前,比如说一个类,一个类的字段,调用的方法,其都是一个字符,而将符号引用解析成直接引用的过程是找到这个方法,这个类,这个属性的一个存储位置的指针。
撰文不易, 期望一个小小的赞.
本文讲的确实不多, 是因为我自己比较喜欢记录自己思考过的东西, 而不是去写一篇"说明书".
下一篇文章仍然会讲述 Class 文件相关的知识