读懂字节码还原JAVA源码
已知有两个类:
public class Father extends GrandFather { public String name = "father";
public void show() {
System.out.println("father");
}
public Father me() {
return this;
}
}
public class Son extends Father {
public void show() {
System.out.println("son");
}
}
把下面字节码转为源码。
// class version 54.0 (54)// access flags 0x21
public class com/demo/Test {
// compiled from: Test.java
// 类的初始化过程都在这。先忽略 init
// access flags 0x1
public <init>()V
L0
LINENUMBER 3 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
RETURN
L1
LOCALVARIABLE this Lcom/demo/Test; L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
// 重点是将下面的转为源码就可以
// access flags 0x9
// 定义了 static void main(String[] args) [出现几次就是几维数组。那么形参名为什么是args?看最下面的 L8的局部变量表可以找出是args
public static main([Ljava/lang/String;)V
L0 // Father son = new Son();
LINENUMBER 5 L0 // 文件的第五行
NEW com/demo/Son // new Son()
DUP // 复制栈顶操作数压入栈
INVOKESPECIAL com/demo/Son.<init> ()V 执行son的 初始化
ASTORE 1 // 把对象存在局部变量表1的位置,也就是L8 中的 Father son
L1
LINENUMBER 7 L1 // 第七行
GETSTATIC java/lang/System.out : Ljava/io/PrintStream; // 调用 System.out 常量
ALOAD 1 // 把局部变量表位置为1的变量压入栈。也就是将Son变量作为下面println的操作数(参数)
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V // 最终结果: System.out.println(son);
L2
LINENUMBER 8 L2
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ALOAD 1
INVOKEVIRTUAL com/demo/Father.me ()Lcom/demo/Father;
// 难点在于:println的操作数是哪一个? son 还是 son.me()。因为这里没有产生其他中间变量。因此猜测是链式调用
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V // 结果 System.out.println(son.me())
L3
LINENUMBER 9 L3
ALOAD 1
INVOKEVIRTUAL com/demo/Father.show ()V // 结果 son.show()
L4
LINENUMBER 10 L4
ALOAD 1
INVOKEVIRTUAL com/demo/Father.me ()Lcom/demo/Father;
INVOKEVIRTUAL com/demo/Father.show ()V // 结果:
L5
LINENUMBER 11 L5
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ALOAD 1
GETFIELD com/demo/Father.name : Ljava/lang/String;
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V // 结果: System.out.println(son.name)
L6
LINENUMBER 12 L6
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ALOAD 1
INVOKEVIRTUAL com/demo/Father.me ()Lcom/demo/Father;
GETFIELD com/demo/Father.name : Ljava/lang/String;
// 难点在于 System.out.println(son.me().name) 还是 System.out.println(son.name)因为两个能匹配下面的方法签名。
// 因为只有一次 ALOAD,这里肯定是前者。后者需要两次ALOAD:son.me(); System.out.println(son.name); 也就是son出现两次
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V // 结果: System.out.println(son.me().name)
L7
LINENUMBER 13 L7
RETURN // 方法结束
L8
LOCALVARIABLE args [Ljava/lang/String; L0 L8 0 // 猜测 L0 L8应该是作用域范围,后面的0则是处于变量表的位置
LOCALVARIABLE son Lcom/demo/Father; L1 L8 1
MAXSTACK = 2 // 这个栈是操作数栈还是虚拟机栈?
MAXLOCALS = 2 // 最多存在多少个局部变量
}
最后结果:
public class Test { public static void main(String[] args) {
Father son = new Son();
System.out.println(son);
System.out.println(son.me());
son.show();
son.me().show();
System.out.println(son.name);
System.out.println(son.me().name);
}
}
还有个问题是,上面没有对应行号。
以上是 读懂字节码还原JAVA源码 的全部内容, 来源链接: utcz.com/z/515856.html