まず、PseudoSketch.javaというファイルを用意する。
public class PseudoSketch {
class A {
private void a() {
System.out.println("a");
}
}
class B extends A {
private void a() {
System.out.println("b");
}
}
public void setup() {
B c = new B();
A d = new B();
c.a();
d.a();
}
public static void main(String args[]) {
PseudoSketch sketch = new PseudoSketch();
sketch.setup();
}
}
次に、コンパイルしてclassファイルを作る。
$ javac PseudoSketch.java
$ ls
PseudoSketch$A.class
PseudoSketch$B.class
PseudoSketch.class
PseudoSketch.java
作成されたclassファイルをjavapコマンドを使って逆アセンブルする。
$ javap -p -c PseudoSketch.class
Compiled from "PseudoSketch.java"
public class PseudoSketch {
public PseudoSketch();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void setup();
Code:
0: new #2 // class PseudoSketch$B
3: dup
4: aload_0
5: invokespecial #3 // Method PseudoSketch$B."<init>":(LPseudoSketch;)V
8: astore_1
9: new #2 // class PseudoSketch$B
12: dup
13: aload_0
14: invokespecial #3 // Method PseudoSketch$B."<init>":(LPseudoSketch;)V
17: astore_2
18: aload_1
19: invokestatic #4 // Method PseudoSketch$B.access$000:(LPseudoSketch$B;)V
22: aload_2
23: invokestatic #5 // Method PseudoSketch$A.access$100:(LPseudoSketch$A;)V
26: return
public static void main(java.lang.String[]);
Code:
0: new #6 // class PseudoSketch
3: dup
4: invokespecial #7 // Method "<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #8 // Method setup:()V
12: return
}
注目するのは、main()でsketch.setup()を呼び出しているときと、 setup()内でc.a()、d.a()を呼びだしている部分の逆アセンブル結果の違い。
sketch.setup()の逆アセンブル結果
8: aload_1
9: invokevirtual #8 // Method setup:()V
c.a()、d.a()の逆アセンブル結果
18: aload_1
19: invokestatic #4 // Method PseudoSketch$B.access$000:(LPseudoSketch$B;)V
22: aload_2
23: invokestatic #5 // Method PseudoSketch$A.access$100:(LPseudoSketch$A;)V