用描述符描述方法时,按照先参数列表,后返回值的顺序来描述。参数列表按照参数的严格顺序放在一组()之内,如方法:String getRealnameByIdAndNickname(int id,String name)的描述符为:(I,Ljava/lang/String;)Ljava/lang/String;
Java是一个单继承,多实现的语言。
访问标志(Access_Flag)


0x0002 private

字段表集合(field_info)


字段表结构:

方法表集合(method_info)

方法表结构:




方法表结构实例:

code结构(code attribute):每一个方法的结构内容



code_length : 之后的字节 会转换成 助记符

附加属性


LineNumberTable : 源代码和行号的对应关系。方便抛异常时,定义出错的位置。
在字节码文件中。 this 属性是默认当做第一个参数给传入进去的。

字节码查看工具 的GitHub地址
idea也有插件。 jclasslib

字节码反编译练习
// 编译下边的文件,然后反编译。对应字节码去翻译一遍,学习 字节码结构。
// 整体的复习。
public class MyTest2{
String str = "welcome";
private int x = 5;
public static Integer in = 10;
public static void main(String[] args){
MyTest2 myTest2 = new MyTest2();
myTest2.setX(8);
in = 20;
}
public void setX(int x){
this.x = x ;
}
}
-- 反编译文件内容为:
➜ jdk8 javap -verbose build.classes.java.main.com.erwa.bytecode.MyTest2
(此时的命令时不打印私有的信息的。需要加上一个 -p 的参数)
警告: 二进制文件build.classes.java.main.com.erwa.bytecode.MyTest2包含com.erwa.bytecode.MyTest2
Classfile /Users/erwa/Desktop/work/workspace/idea/jdk8/build/classes/java/main/com/erwa/bytecode/MyTest2.class
Last modified 2020-2-13; size 833 bytes
MD5 checksum c119f2eae8cb307e9b90f835d460a4ad
Compiled from "MyTest2.java"
public class com.erwa.bytecode.MyTest2
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref
#10.#34
// java/lang/Object."<init>":()V
#2 = String
#35
// welcome
#3 = Fieldref
#5.#36
// com/erwa/bytecode/MyTest2.str:Ljava/lang/String;
#4 = Fieldref
#5.#37
// com/erwa/bytecode/MyTest2.x:I
#5 = Class
#38
// com/erwa/bytecode/MyTest2
#6 = Methodref
#5.#34
// com/erwa/bytecode/MyTest2."<init>":()V
#7 = Methodref
#5.#39
// com/erwa/bytecode/MyTest2.setX:(I)V
#8 = Methodref
#40.#41
// java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
#9 = Fieldref
#5.#42
// com/erwa/bytecode/MyTest2.in:Ljava/lang/Integer;
#10 = Class
#43
// java/lang/Object
#11 = Utf8
str
#12 = Utf8
Ljava/lang/String;
#13 = Utf8
x
#14 = Utf8
I
#15 = Utf8
in
#16 = Utf8
Ljava/lang/Integer;
#17 = Utf8
<init>
#18 = Utf8
()V
#19 = Utf8
Code
#20 = Utf8
LineNumberTable
#21 = Utf8
LocalVariableTable
#22 = Utf8
this
#23 = Utf8
Lcom/erwa/bytecode/MyTest2;
#24 = Utf8
main
#25 = Utf8
([Ljava/lang/String;)V
#26 = Utf8
args
#27 = Utf8
[Ljava/lang/String;
#28 = Utf8
myTest2
#29 = Utf8
setX
#30 = Utf8
(I)V
#31 = Utf8
<clinit>
#32 = Utf8
SourceFile
#33 = Utf8
MyTest2.java
#34 = NameAndType
#17:#18
// "<init>":()V
#35 = Utf8
welcome
#36 = NameAndType
#11:#12
// str:Ljava/lang/String;
#37 = NameAndType
#13:#14
// x:I
#38 = Utf8
com/erwa/bytecode/MyTest2
#39 = NameAndType
#29:#30
// setX:(I)V
#40 = Class
#44
// java/lang/Integer
#41 = NameAndType
#45:#46
// valueOf:(I)Ljava/lang/Integer;
#42 = NameAndType
#15:#16
// in:Ljava/lang/Integer;
#43 = Utf8
java/lang/Object
#44 = Utf8
java/lang/Integer
#45 = Utf8
valueOf
#46 = Utf8
(I)Ljava/lang/Integer;
{
java.lang.String str;
descriptor: Ljava/lang/String;
flags:
public static java.lang.Integer in;
descriptor: Ljava/lang/Integer;
flags: ACC_PUBLIC, ACC_STATIC
public com.erwa.bytecode.MyTest2();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1
// Method java/lang/Object."<init>":()V
4: aload_0
5: ldc
#2
// String welcome
7: putfield
#3
// Field str:Ljava/lang/String;
10: aload_0
11: iconst_5
12: putfield
#4
// Field x:I
15: return
LineNumberTable:
line 3: 0
line 4: 4
line 5: 10
LocalVariableTable:
Start Length Slot Name Signature
0
16
0 this Lcom/erwa/bytecode/MyTest2;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: new
#5
// class com/erwa/bytecode/MyTest2
3: dup
4: invokespecial #6
// Method "<init>":()V
7: astore_1
8: aload_1
9: bipush
8
11: invokevirtual #7
// Method setX:(I)V
14: bipush
20
16: invokestatic #8
// Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
19: putstatic
#9
// Field in:Ljava/lang/Integer;
22: return
LineNumberTable:
line 8: 0
line 9: 8
line 10: 14
line 11: 22
LocalVariableTable:
Start Length Slot Name Signature
0
23
0 args [Ljava/lang/String;
8
15
1 myTest2 Lcom/erwa/bytecode/MyTest2;
public void setX(int);
descriptor: (I)V
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: iload_1
2: putfield
#4
// Field x:I
5: return
LineNumberTable:
line 13: 0
line 14: 5
LocalVariableTable:
Start Length Slot Name Signature
0
6
0 this Lcom/erwa/bytecode/MyTest2;
0
6
1
x I
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: bipush
10
2: invokestatic #8
// Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: putstatic
#9
// Field in:Ljava/lang/Integer;
8: return
LineNumberTable:
line 6: 0
}
SourceFile: "MyTest2.java"
加上synchronized关键字字节码
为什么除了访问权限多了synchronized之外,其他的地方都一样。
因为synchronized使用的方式有多种:
如果synchronized用在实例方法上,表示对此方法加了一个锁。
如果用在了对象上,则字节码会生成锁的入口和出口。
synchronized用在类中的静态方法上, 其实是给当前的类的对象上的锁。
关于锁:能不用synchronized就不用。使用轻量级的 lock 。
构造方法和静态代码块字节码
如果没有设置构造方法,会默认生成一个无参构造。如果有静态方法,会初始化一个clinit的方法。
全局变量是在构造方法初始化的时候完成赋值的。
一个构造方法对应一个init方法。(全局变量的赋值,会在每个init方法中都重新执行一次)