Java字节码 .class文件
Java语言的write once run anywhere能够得以实现其中重要的部分就是关于其字节码的生成,Java虚拟机JVM通过将字节码文件解析到各种不同的平台对应的机器语言来实现了其跨平台的特性。那么如何才能看到字节码,又如何来读懂它呢。
关于.class文件,其实还有很多其他的语言也是基于这一特点的,例如Scala、groovy、kotlin等,都是基于jvm这一平台,通过相应的编译器生成了.class文件。下面给一个简单地例子:
public class ClassTest {
private int a;
public void print() {
System.out.println(this.a);
}
}
使用javac 文件名.java对其进行编译得到.class文件
本文先使用notepad++ 直接查看其字节码文件,需要先安装HEX-Editor插件
安装后notepad++会重启,然后选择使用view in HEX来查看.class文件
16bit查看效果
“真好看”
类型 | 名称 | 数量 |
u4 | magic | 1 |
u2 | minor_version | 1 |
u2 | major_version | 1 |
u2 | constant_pool_count | 1 |
cp_info | constant_pool | constant_pool_count-1 |
u2 | access_flags | 1 |
u2 | this_class | 1 |
u2 | super_class | 1 |
u2 | interfaces_count | 1 |
u2 | interfaces | interfaces_count |
u2 | fields_count | 1 |
field_info | fields | fields_count |
u2 | methods_count | 1 |
method_info | methods | methods_count |
u2 | attributes_count | 1 |
attribute_info | attributes | attributes_count |
开头的cafe babe可以看成是一种文件的前缀,通过和这个标识才可以被jvm接受。
紧接着的00 00 00 34表示52(10),java版本号从45开始,JDK1.0-1.1--(45.0~45.3) 1.2--46 1.3--47 1.4--48 1.5--49 1.6--50 1.7--51 1.8--52
这样来看字节码好像也得不到什么其他有效的信息了,因此可以通过使用javap命令来反编译字节码文件
使用命令javap -v -p ClassTest.class
// 描述.class文件所在位置
Classfile /D:/IdeaWorkspace/XXXXX/XXXXXX/src/main/java/com/XXXX/XXXX/ClassTest.class
// 字面含义
Last modified 2019-8-8; size 413 bytes
MD5 checksum df5d2b60f97b3930a43c8a375b00340a
Compiled from "ClassTest.java"
public class com.XXXX.XXXX.ClassTest
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#16 // java/lang/Object."<init>":()V
#2 = Fieldref #17.#18 // java/lang/System.out:Ljava/io/PrintStream;
#3 = Fieldref #5.#19 // com/XXXX/XXXX/ClassTest.a:I
#4 = Methodref #20.#21 // java/io/PrintStream.println:(I)V
#5 = Class #22 // com/XXXX/XXXX/ClassTest
#6 = Class #23 // java/lang/Object
#7 = Utf8 a
#8 = Utf8 I
#9 = Utf8 <init>
#10 = Utf8 ()V
#11 = Utf8 Code
#12 = Utf8 LineNumberTable
#13 = Utf8 print
#14 = Utf8 SourceFile
#15 = Utf8 ClassTest.java
#16 = NameAndType #9:#10 // "<init>":()V
#17 = Class #24 // java/lang/System
#18 = NameAndType #25:#26 // out:Ljava/io/PrintStream;
#19 = NameAndType #7:#8 // a:I
#20 = Class #27 // java/io/PrintStream
#21 = NameAndType #28:#29 // println:(I)V
#22 = Utf8 com/XXXX/XXXX/ClassTest
#23 = Utf8 java/lang/Object
#24 = Utf8 java/lang/System
#25 = Utf8 out
#26 = Utf8 Ljava/io/PrintStream;
#27 = Utf8 java/io/PrintStream
#28 = Utf8 println
#29 = Utf8 (I)V
{
private int a;
descriptor: I
flags: ACC_PRIVATE
public com.XXXX.XXXX.ClassTest();
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 3: 0
public void print();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #3 // Field a:I
7: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
10: return
LineNumberTable:
line 8: 0
line 9: 10
}
SourceFile: "ClassTest.java"
内容、关键字:
flags(access_flags):ACC_PUBLIC, ACC_SUPER 表示的是类的访问标志,这个访问标志用于识别一些类或者接口层次的访问信息,包括:这个Class是类还是接口;是否定义为public类型;是否定义为abstract类型;如果是类,是否被声明为final类型
其中的invokespecial为jvm指令
标志名 标志值 含义 ACC_PUBLIC 0x0001 是否为public类型
ACC_FINAL
0x0010 是否为final类型,只有类可以设置 ACC_SUPER 0x0020 是否允许使用invokespecial字节码指令的新语义.
ACC_INTERFACE 0x0200 标志这是一个接口 ACC_ABSTRACT 0x0400 是否为abstract类型,对于接口或者抽象类来说,
次标志值为真,其他类型为假ACC_SYNTHETIC 0x1000 标志这个类并非由用户代码产生 ACC_ANNOTATION 0x2000 标志这是一个注解 ACC_ENUM 0x4000 标志这是一个枚举
指令 说明 invokevirtual 用于调用对象的实例方法,根据对象的实际类型进行分派 invokeinterface 用于调用接口方法,会在运行时搜索一个实现了这个接口方法的对象,找出合适的方法进行调用 invokespecial 用于调用一些需要特殊处理的实例方法,包括初始化方法、私有方法、父类方法 invokestatic 用于调用类方法 invokedynamic 用于在运行时动态解析出调用点限定符所引用的方法,并执行该方法