认识class文件

二进制字节流
硬盘上的文件都是以二进制流的方式存储的
类文件结构
- Magic Number 魔数 文件类型, .class的16进制魔数就是cafebabe
- minor version+major version 小大版本号 jdk8对应的版本号是52,其16进制是0034
- constant_poll_count 常量池中常量数量
- constant_poll 常量池是一个长度=constant_poll_count-1的列表,保留了空的0位
- 权限标识
- 类名 、父类
- 接口数量 所有接口
- 成员变量总数 成员变量
- 方法总数 方法
-
演示
8 public class ClassViewer {9 private int c;10 public String add(String a){11 int i =0 ,j=10;12 i++;13 c += j;14 return a+i+j;15 }16}
16进制
Javap -v ClassViewer.class
下述class类信息块 所说的栈 和 栈顶,不做特殊说明 都指的是操作数栈
Last modified 2021-3-30; size 558 bytesMD5 checksum 9007716478deccca8618ff428c3c57dcCompiled from "ClassViewer.java"public class com.?.ClassViewerminor version: 0major version: 52 //jdk的MarjorVersion=52flags: ACC_PUBLIC, ACC_SUPER // 访问权限修饰符Constant pool:#1 = Methodref #9.#20 // java/lang/Object."<init>":()V#2 = Fieldref #8.#21 // com/zhz/jvm/ClassViewer.c:I#3 = Class #22 // java/lang/StringBuilder#4 = Methodref #3.#20 // java/lang/StringBuilder."<init>":()V#5 = Methodref #3.#23 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;#6 = Methodref #3.#24 // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;#7 = Methodref #3.#25 // java/lang/StringBuilder.toString:()Ljava/lang/String;#8 = Class #26 // thisClass: com/?/ClassViewer#9 = Class #27 // superClass: java/lang/Object#10 = Utf8 c#11 = Utf8 I#12 = Utf8 <init>#13 = Utf8 ()V#14 = Utf8 Code#15 = Utf8 LineNumberTable#16 = Utf8 add#17 = Utf8 (Ljava/lang/String;)Ljava/lang/String;#18 = Utf8 SourceFile#19 = Utf8 ClassViewer.java#20 = NameAndType #12:#13 // "<init>":()V#21 = NameAndType #10:#11 // c:I#22 = Utf8 java/lang/StringBuilder#23 = NameAndType #28:#29 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;#24 = NameAndType #28:#30 // append:(I)Ljava/lang/StringBuilder;#25 = NameAndType #31:#32 // toString:()Ljava/lang/String;#26 = Utf8 com/zhz/jvm/ClassViewer#27 = Utf8 java/lang/Object#28 = Utf8 append#29 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;#30 = Utf8 (I)Ljava/lang/StringBuilder;#31 = Utf8 toString#32 = Utf8 ()Ljava/lang/String;{public com.?.ClassViewer();descriptor: ()V //构造方法flags: ACC_PUBLIC //公共Code:stack=1, locals=1, args_size=1 // 默认无参构造:使用1个栈Object.init,1个本地变量(全局变量c),1个参数(也是c)0: aload_0 // 把局部变量表0位元素,也就是c 加载到操作数栈栈顶1: invokespecial #1 // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 8: 0public java.lang.String add(java.lang.String);descriptor: (Ljava/lang/String;)Ljava/lang/String;flags: ACC_PUBLICCode:stack=3, locals=4, args_size=2 // 本方法调用了3个栈帧,4个局部变量(a,i,j,c),2个参数(a和c)0: iconst_0 // 常数0压入操作数栈1: istore_2 // 将栈顶元素 保存到局部变量表2的位置2: bipush 10 //取值 10 压入栈顶4: istore_3 // 栈顶元素 保存到局部变量表index=3的位置5: iinc 2, 1 // 取局部变量表index=2的元素,做+1操作8: aload_0 //加载局部变量表index=0的值 到栈顶9: dup10: getfield #2 // Field c:I 获取#2的全局变量值 =0 压入栈顶13: iload_3 //加载局部变量表index=3的值到栈顶14: iadd //取栈顶两个元素做++操作15: putfield #2 // Field c:I 赋值18: new #3 // class java/lang/StringBuilder21: dup22: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V25: aload_126: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;29: iload_230: invokevirtual #6 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;33: iload_334: invokevirtual #6 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;37: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;40: areturnLineNumberTable:line 11: 0line 12: 5line 13: 8line 14: 18}SourceFile: "ClassViewer.java"
JVM指令
load 将局部变量表指定位置的数据 压入 操作数栈 栈顶
- store 将操作数栈 栈顶元素 保存到局部变量表指定位置
- iconst/bipush/sipush/ldc 取值指令,根据取值大小用不同指令,?/byte/short/?/int用ldc,
- iinc 自增
- invokevisual/invokestatic/invokespecial 调用实例方法/调用静态类方法/调用特殊如构造方法
- ireturn 返回
Java ASM框架
ASM API基于访问者模式,为我们提供了ClassVisitor,MethodVisitor,FieldVisitor API接口,每当ASM扫描到类字段是会回调visitField方法,扫描到类方法是会回调MethodVisitor.
//todo
aop的静态织入,还有spring 使用真实参数名(正常class编译是arg0,arg1),也是asm技术。
