读懂字节码还原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

回到顶部