系列课程, 待更新......
前半节都学完了,我们已经很深入了,后面讲的和实际离的远,可以继续学习

第一章 Class文件简介和发展历史
1.1Class文件简介

接下来一段时间,我们将要进行枯燥有趣的看二进制的.class文件,
1.2 Class文件结构概述




1.3 Class文件设计理念以及意义
它们都是自己定义语法规范,编译器,把语言编译成jvm认识的字节码.CLASS文件执行
![]()
第二章、Class文件结构
2.1 文件结构 - 魔数(magic value)

也就是读取class文件第一行十六进制文件的含义,
KAFEBABE : 指的是class文件类型,对应十进制188...., 如果不匹配,就不是class文件
00 34: 对应十进制 52, 则得知jdk版本是1.8版本,主版本号


2.2 文件结构 - 常量池字节码数据的读取规则
接下来再按照字节长度往下面计算,两个字节代表常量池数量,
00 1D: 对应十进制是29(1 - 28)

class文件由无符号数和表组成
cp_info的结构: tag表示类型,首先根据tag值,验证出是哪一个类型, 根据类型找到类型的表,然后接着往下读。info[] 表示表的集合,里面可嵌套info

下面列出的就是info里面嵌套的一些info ,罗列几个




下面是罗列详细的前18个常量池 和class文件对应起来,说明常量池的解析过程:
1D: 说明有29 个常量,
0A: 一个字节 10, 表示10号常量类型,
对应 constant_methodref_info(0A 0006 000F) ---> #1 = Methodref #6.#15 ;
它后面有四个字节,读完constant_methodref_info之后,再读一个字节的tag位
09: constant_fielderref_info(09 0010 0011) ---> #2 = Fieldref #16.#17 ;
08: constant_Stringref_info(08 0012) ---> #3 = String #18 // hello, youngpeng
.......直到读取完29个常量池................


知道了解析常量池的规则,我们就可以用工具来解析: java -verbose xxx.class
C:\Users\Administrator>cd Desktop
C:\Users\Administrator\Desktop>javap -verbose Hello.class
Classfile /C:/Users/Administrator/Desktop/Hello.class
Last modified 2019-10-12; size 420 bytes
MD5 checksum 66f7607a3c46c2e4a43d39d116856371
Compiled from "Hello.java"
public class Hello
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#15 // java/lang/Object."<init>":()V
#2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #18 // hello, youngpeng
#4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #21 // Hello
#6 = Class #22 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 main
#12 = Utf8 ([Ljava/lang/String;)V
#13 = Utf8 SourceFile
#14 = Utf8 Hello.java
#15 = NameAndType #7:#8 // "<init>":()V
#16 = Class #23 // java/lang/System
#17 = NameAndType #24:#25 // out:Ljava/io/PrintStream;
#18 = Utf8 hello, youngpeng
#19 = Class #26 // java/io/PrintStream
#20 = NameAndType #27:#28 // println:(Ljava/lang/String;)V
#21 = Utf8 Hello
#22 = Utf8 java/lang/Object
#23 = Utf8 java/lang/System
#24 = Utf8 out
#25 = Utf8 Ljava/io/PrintStream;
#26 = Utf8 java/io/PrintStream
#27 = Utf8 println
#28 = Utf8 (Ljava/lang/String;)V
{
public Hello();
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 1: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String hello, youngpeng
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 3: 0
line 4: 8
}
SourceFile: "Hello.java"
C:\Users\Administrator\Desktop>
2.3 文件结构 - 访问标识(access flag)
1. 这个是旧的(请忽略,参考而已)

这个位数才是新的

2. 先写一段程序接口
public interface Interf{
public static void main(String args[]){
}
}
3. 分析class文件
C:\Users\Administrator\Desktop>javap -verbose Interf.class
Classfile /C:/Users/Administrator/Desktop/Interf.class
Last modified 2019-10-12; size 189 bytes
MD5 checksum 304913b072ab554619c41b23c1fd4339
Compiled from "Interf.java"
public interface Interf
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
Constant pool:
#1 = Class #9 // Interf
#2 = Class #10 // java/lang/Object
#3 = Utf8 main
#4 = Utf8 ([Ljava/lang/String;)V
#5 = Utf8 Code
#6 = Utf8 LineNumberTable
#7 = Utf8 SourceFile
#8 = Utf8 Interf.java
#9 = Utf8 Interf
#10 = Utf8 java/lang/Object
{
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 4: 0
}
SourceFile: "Interf.java"
C:\Users\Administrator\Desktop>
4.分析class字节码
flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
interface = ACC_PUBLIC + ACC_INTERFACE + ACC_ABSTRACT
0601 = 0001 + 0200 + 0400
2.4 文件结构 - 类索引(class 文件格式)
介绍完class文件标识cafababe, 常量池,和文件标识(public,interface,abstract)之后,紧跟着就是
1. 内容

2. 编写程序
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class Hello1 extends FileInputStream implements Runnable, ActionListener {
public Hello1(String name) throws FileNotFoundException {
super(name);
}
@Override
public void actionPerformed(ActionEvent e) {
}
@Override
public void run() {
}
}
3. 用工具分析查看
C:\Users\Administrator\Desktop>javap -verbose Hello1.class
Classfile /C:/Users/Administrator/Desktop/Hello1.class
Last modified 2019-10-13; size 479 bytes
MD5 checksum 2a88671d70b6efb7d259ca4043f13edd
Compiled from "Hello1.java"
public class Hello1 extends java.io.FileInputStream implements java.lang.Runnable,java.awt.event.ActionListener
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #3.#18 // java/io/FileInputStream."<init>":(Ljava/lang/String;)V
#2 = Class #19 // Hello1
#3 = Class #20 // java/io/FileInputStream
#4 = Class #21 // java/lang/Runnable
#5 = Class #22 // java/awt/event/ActionListener
#6 = Utf8 <init>
#7 = Utf8 (Ljava/lang/String;)V
#8 = Utf8 Code
#9 = Utf8 LineNumberTable
#10 = Utf8 Exceptions
#11 = Class #23 // java/io/FileNotFoundException
#12 = Utf8 actionPerformed
#13 = Utf8 (Ljava/awt/event/ActionEvent;)V
#14 = Utf8 run
#15 = Utf8 ()V
#16 = Utf8 SourceFile
#17 = Utf8 Hello1.java
#18 = NameAndType #6:#7 // "<init>":(Ljava/lang/String;)V
#19 = Utf8 Hello1
#20 = Utf8 java/io/FileInputStream
#21 = Utf8 java/lang/Runnable
#22 = Utf8 java/awt/event/ActionListener
#23 = Utf8 java/io/FileNotFoundException
{
public Hello1(java.lang.String) throws java.io.FileNotFoundException;
descriptor: (Ljava/lang/String;)V
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: invokespecial #1 // Method java/io/FileInputStream."<init>":(Ljava/lang/String;)V
5: return
LineNumberTable:
line 9: 0
line 10: 5
Exceptions:
throws java.io.FileNotFoundException
public void actionPerformed(java.awt.event.ActionEvent);
descriptor: (Ljava/awt/event/ActionEvent;)V
flags: ACC_PUBLIC
Code:
stack=0, locals=2, args_size=2
0: return
LineNumberTable:
line 13: 0
public void run();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=0, locals=1, args_size=1
0: return
LineNumberTable:
line 16: 0
}
SourceFile: "Hello1.java"
C:\Users\Administrator\Desktop>
4. 分析过程:

常量池结束位置: #23 = Utf8 java/io/FileNotFoundException
access_flags结束位置0021: flags: ACC_PUBLIC, ACC_SUPER --> 0001 or 0020 = 0021
2.5 文件结构 -字段表集合
1. 字段访问标识

2. 字段表集合

3. 写一段程序
public class Hello{
private int a;
public byte b;
public static Object obj;
protected Object[] objs;
}
4. 工具分析
C:\Users\Administrator\Desktop>javap -verbose Hello.class
Classfile /C:/Users/Administrator/Desktop/Hello.class
Last modified 2019-10-13; size 288 bytes
MD5 checksum 7b5c6270785b47745b8e50db5048cda2
Compiled from "Hello.java"
public class Hello
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #3.#18 // java/lang/Object."<init>":()V
#2 = Class #19 // Hello
#3 = Class #20 // java/lang/Object
#4 = Utf8 a
#5 = Utf8 I
#6 = Utf8 b
#7 = Utf8 B
#8 = Utf8 obj
#9 = Utf8 Ljava/lang/Object;
#10 = Utf8 objs
#11 = Utf8 [Ljava/lang/Object;
#12 = Utf8 <init>
#13 = Utf8 ()V
#14 = Utf8 Code
#15 = Utf8 LineNumberTable
#16 = Utf8 SourceFile
#17 = Utf8 Hello.java
#18 = NameAndType #12:#13 // "<init>":()V
#19 = Utf8 Hello
#20 = Utf8 java/lang/Object
5. 分析过程

#4 = Utf8 a 变量a
#5 = Utf8 I int类型
#6 = Utf8 b 变量b
#7 = Utf8 B byte类型
#8 = Utf8 obj 变量obj
#9 = Utf8 Ljava/lang/Object; L开头,引用类型
#10 = Utf8 objs
#11 = Utf8 [Ljava/lang/Object; [L 开头,数组类型
得一个一个往下数:
2.6 文件结构 - 方法表集合
1. 方法表数据结构

2. 接着数对应的class文档
常量池结束 -- 访问标志 -- 类索引 -- 父类索引 -- 接口 -- 字段表长度 -- 方法表长度(默认加构造方法) -- 方法表结构【access_flags,...】
3. 写几个方法
public class Hello1{
private int add (int a, int b){
return a + b;
}
public String append(String s, Object l){
return s + l;
}
}
4. 工具查看
C:\Users\Administrator\Desktop>javap -verbose Hello.class
Classfile /C:/Users/Administrator/Desktop/Hello.class
Last modified 2019-10-13; size 559 bytes
MD5 checksum e0d7132926bc95d3b6360d77abde2a5f
Compiled from "Hello.java"
public class Hello
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #8.#19 // java/lang/Object."<init>":()V
#2 = Class #20 // java/lang/StringBuilder
#3 = Methodref #2.#19 // java/lang/StringBuilder."<init>":()V
#4 = Methodref #2.#21 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#5 = Methodref #2.#22 // java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
#6 = Methodref #2.#23 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#7 = Class #24 // Hello
#8 = Class #25 // java/lang/Object
#9 = Utf8 <init>
#10 = Utf8 ()V
#11 = Utf8 Code
#12 = Utf8 LineNumberTable
#13 = Utf8 add
#14 = Utf8 (II)I
#15 = Utf8 append
#16 = Utf8 (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;
#17 = Utf8 SourceFile
#18 = Utf8 Hello.java
#19 = NameAndType #9:#10 // "<init>":()V
#20 = Utf8 java/lang/StringBuilder
#21 = NameAndType #15:#26 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#22 = NameAndType #15:#27 // append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
#23 = NameAndType #28:#29 // toString:()Ljava/lang/String;
#24 = Utf8 Hello
#25 = Utf8 java/lang/Object
#26 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#27 = Utf8 (Ljava/lang/Object;)Ljava/lang/StringBuilder;
#28 = Utf8 toString
#29 = Utf8 ()Ljava/lang/String;
{
public Hello();
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 1: 0
public int add(int, int);
descriptor: (II)I
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=3
0: iload_1
1: iload_2
2: iadd
3: ireturn
LineNumberTable:
line 3: 0
public java.lang.String append(java.lang.String, java.lang.Object);
descriptor: (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=3
0: new #2 // class java/lang/StringBuilder
3: dup
4: invokespecial #3 // Method java/lang/StringBuilder."<init>":()V
7: aload_1
8: invokevirtual #4 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
11: aload_2
12: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
15: invokevirtual #6 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
18: areturn
LineNumberTable:
line 6: 0
}
SourceFile: "Hello.java"
code: 内容是方法体的内容
()V: 表示 方法返回值空void
(I I)I: 表示 方法返回值空int, 两个int 参数
2.7 文件结构 - 属性表集合
方法表存储的是方法表的摘要信息,方法体的信息存储在属性表的code信息中。
属性表就是描述其他一些额外信息的,自定义和外定义属性,方法表属性表示不了了,用属性表补充。
1. 大概理解意思

2. 属性表结构

3. code表结构

第三章 字节码部分
学完字节码格式 -> 接下来关注字节码指令
-
学完字节码格式
-
字节码指令
-
类加载机制
-
虚拟机字节码执行引擎
3.1 字节码指令简介

3.2 两种不同的架构执行 1 + 1 = 2 时的操作

3.3 字节码和数据类型

3.4 加载和存储指令

public class Hello{
int add(int a, int b){
int c = 1 + 3;
return a + b;
}
}
{
int add(int, int);
descriptor: (II)I
flags:
Code:
stack=2, locals=4, args_size=3
0: iconst_4
1: istore_3
2: iload_1
3: iload_2
4: iadd
5: ireturn
LineNumberTable:
line 3: 0
line 4: 2
}
我们会发现 参数总会多一个,那是因为默认会带一个 this 这个参数
istore 是编译器优化,编译时期直接将静态变量运算了
3.7 类加载指令-对象的创建和访问


本文详细解析了Java Class文件的结构,包括魔数、常量池、访问标识、类索引、字段表集合、方法表集合及属性表集合等关键组成部分。通过实例展示了如何使用javap工具分析Class文件,深入理解字节码格式与执行机制。
270

被折叠的 条评论
为什么被折叠?



