Class文件是啥,有什么用和字节码有什么关系
Posted 写Bug的渣渣高
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Class文件是啥,有什么用和字节码有什么关系相关的知识,希望对你有一定的参考价值。
本文概述:
在上一个文章我们谈论了关于字节码的信息.
这里我们来看看 Class 文件是什么
Class 文件是什么
还是老样子, 不直接说是啥, 应该先了解为什么需要.
其实你看这个名字就了解到了, 肯定和类有关系. 我们思考一下, 我们学习的时候肯定都有了解过, Java 是跨平台的, 那么如何跨平台呢?
答案就是这个 Class 文件, 名字很明显, 保存的就是一个类的信息.
也就是说, 这个 Class 文件保存了一个类的所有信息, Class 文件是可以开平台的 (注: 拓展思考一下, 你可能了解过 JVM 里面也有 Class 类信息, 事实上,Class 类信息就是根据这个 Class 文件而产生的, 所以说不同平台, 虚拟机不同, 只要虚拟机是按照规范设计的, 就可以加载这个 Class 文件.)
Class 文件里面存储了类的所有信息, 其实到这里, 一定要做的是,Class 文件里面 <都需要存储哪些东西>,如果你知道, 也不要去回忆他存储哪些东西, <最重要的是,一个Class里面有哪些信息>
由问题来思考需要保存哪些信息:
- 版本问题: 1.8 和 1.7 的 Class 文件能通用吗?, 肯定不行, 那么就需要保存版本号
- 下面分析就简单多了, 我们编写类的时候有啥
- 类信息: 类全限定名, 修饰符, 如果实现了父类, 是不是还需要父类的全限定名和修饰符. 或者说实现了接口, 接口的全限定名, 修饰符等
- 方法信息: 一个类中有很多方法吧, 方法名, 修饰符, 返回值, 参数.
- 属性信息: 比如说基本数据类型, 引用对象等等.
有个很重要的就是: 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"
结论:
- final 修饰的 8 种基本类型的值, 会全部进入常量池
- 非 final 修饰的, 也包括使用了 static 的情况, 只有 double, float,long 的值会进入常量池
- 所有字符串类型字面量 这里在以后的文章中的 StringTable 会讲, 很有趣
先看看如何存储 int 和 float 数据类型的变量
我们只需要了解, 对于这类 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 如何存储的,
看看这里,String 是一个引用, 他只需要保存目标字符串的序号即可.
在看一个图片:
引用类型如何存储的
经过 String 的分析, 你可能有一些头绪了. 引用类型, 是也给引用指向在堆中分配的实例数据.Class 就是一个文本哇, 哪里有堆?
来看看引用类型的结构:
这个是不是和 String 很像, 保存的依然是序号.
那这个序号存储的是啥呢?
符号引用: 不知道你看见这个的时候, 有没有想到类加载阶段的解析阶段. 对的, 没错, 就是哪个.
符号引用是啥呢:
符号引用本质上就是字符串, 但是这个字符串是具有意义的, 根据这个字符串能找到目标.
比如说类的全限定名: com.ggzx.User, 当然接口也可以.
在类加载的解析阶段: 会把符号引用解析成直接引用
解析动作主要针对于接口字段类方法接口方法,方法类型,因为没有将符号引用替换之前,比如说一个类,一个类的字段,调用的方法,其都是一个字符,而将符号引用解析成直接引用的过程是找到这个方法,这个类,这个属性的一个存储位置的指针。
撰文不易, 期望一个小小的赞.
本文讲的确实不多, 是因为我自己比较喜欢记录自己思考过的东西, 而不是去写一篇"说明书".
下一篇文章仍然会讲述 Class 文件相关的知识
以上是关于Class文件是啥,有什么用和字节码有什么关系的主要内容,如果未能解决你的问题,请参考以下文章