這是一篇修改class檔案的文章。註釋並不完全,要抓住這次練習的目的:
boolean在虛擬機器中是以何種方式解讀的
好的,開始我的表演
1.安裝asmtools.jar(本文尾部有步驟)
2.編寫一個java檔案,並編譯,執行
2.1 Foo.java
1 public class Foo { 2 public static void main(String[] args) { 3 boolean flag = true; 4 if (flag) { 5 System.out.println("Hello, Java!"); 6 } 7 if (flag == true) { 8 System.out.println("Hello, JVM!"); 9 } 10 } 11 }
2.2 編譯並執行
[root@localhost tmp]# javac Foo.java [root@localhost tmp]# java Foo Hello, Java! Hello, JVM!
3.檢視編譯後的java檔案,class (注意看黃色部分的變化)
[root@localhost tmp]# javap -verbose Foo Classfile /usr/local/asmtools-7.0-build/binaries/lib/tmp/Foo.class Last modified Aug 12, 2019; size 493 bytes MD5 checksum d51944604c5b4e45cb895501910347ea Compiled from "Foo.java" public class Foo minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #7.#17 // java/lang/Object."<init>":()V #2 = Fieldref #18.#19 // java/lang/System.out:Ljava/io/PrintStream; #3 = String #20 // Hello, Java! #4 = Methodref #21.#22 // java/io/PrintStream.println:(Ljava/lang/String;)V #5 = String #23 // Hello, JVM! #6 = Class #24 // Foo #7 = Class #25 // java/lang/Object #8 = Utf8 <init> #9 = Utf8 ()V #10 = Utf8 Code #11 = Utf8 LineNumberTable #12 = Utf8 main #13 = Utf8 ([Ljava/lang/String;)V #14 = Utf8 StackMapTable #15 = Utf8 SourceFile #16 = Utf8 Foo.java #17 = NameAndType #8:#9 // "<init>":()V #18 = Class #26 // java/lang/System #19 = NameAndType #27:#28 // out:Ljava/io/PrintStream; #20 = Utf8 Hello, Java! #21 = Class #29 // java/io/PrintStream #22 = NameAndType #30:#31 // println:(Ljava/lang/String;)V #23 = Utf8 Hello, JVM! #24 = Utf8 Foo #25 = Utf8 java/lang/Object #26 = Utf8 java/lang/System #27 = Utf8 out #28 = Utf8 Ljava/io/PrintStream; #29 = Utf8 java/io/PrintStream #30 = Utf8 println #31 = Utf8 (Ljava/lang/String;)V { public Foo(); 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 1: 0 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: iconst_1 //常量1入棧 1: istore_1 //把棧頂值儲存到區域性變數表下標為1的位置,即flag =1; 2: iload_1 //取區域性變數表中下標為1的變數壓棧 3: ifeq 14 //(jump if i == 0) 將棧頂值與0比較,如果相等,則跳入14步驟。 6: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 9: ldc #3 // String Hello, Java! 11: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 14: iload_1 //取區域性變數表中下標為1的變數壓棧 15: iconst_1 //常量1入棧 16: if_icmpne 27 //比較棧頂兩個值,如果不相等,則跳轉到27 19: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 22: ldc #5 // String Hello, JVM! 24: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 27: return LineNumberTable: line 3: 0 line 4: 2 line 5: 6 line 7: 14 line 8: 19 line 10: 27 StackMapTable: number_of_entries = 2 frame_type = 252 /* append */ offset_delta = 14 locals = [ int ] frame_type = 12 /* same */ } SourceFile: "Foo.java"
4.使用asmtools.jar修改class檔案
[root@localhost tmp]# java -jar ../asmtools.jar jdis Foo.class > Foo.jasm.1 [root@localhost tmp]# ls Foo.class Foo.jasm.1 Foo.java [root@localhost tmp]# java -cp ../asmtools.jar org.openjdk.asmtools.jdis.Main Foo.class > Foo.jasm.1 [root@localhost tmp]# awk 'NR==1,/iconst_1/{sub(/iconst_1/,"iconst_2")} 1' Foo.jasm.1 > Foo.jasm [root@localhost tmp]# ls Foo.class Foo.jasm Foo.jasm.1 Foo.java [root@localhost tmp]# java Foo Hello, Java! Hello, JVM!
5.再次編譯,執行
[root@localhost tmp]# java -jar ../asmtools.jar jasm Foo.jasm [root@localhost tmp]# java Foo Hello, Java! [root@localhost tmp]# ls Foo.class Foo.jasm Foo.jasm.1 Foo.java
6.檢視修改後的class檔案
[root@localhost tmp]# javap -verbose Foo Classfile /usr/local/asmtools-7.0-build/binaries/lib/tmp/Foo.class Last modified Aug 12, 2019; size 431 bytes MD5 checksum 18cfb8b8b7d9d49e9ffce213e70c8898 Compiled from "Foo.jasm" public class Foo minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = String #10 // Hello, Java! #2 = String #11 // Hello, JVM! #3 = Fieldref #27.#12 // java/lang/System.out:Ljava/io/PrintStream; #4 = Methodref #8.#17 // java/lang/Object."<init>":()V #5 = Methodref #13.#30 // java/io/PrintStream.println:(Ljava/lang/String;)V #6 = Utf8 (Ljava/lang/String;)V #7 = Utf8 out #8 = Class #9 // java/lang/Object #9 = Utf8 java/lang/Object #10 = Utf8 Hello, Java! #11 = Utf8 Hello, JVM! #12 = NameAndType #7:#23 // out:Ljava/io/PrintStream; #13 = Class #15 // java/io/PrintStream #14 = Utf8 ([Ljava/lang/String;)V #15 = Utf8 java/io/PrintStream #16 = Utf8 main #17 = NameAndType #29:#20 // "<init>":()V #18 = Utf8 SourceFile #19 = Utf8 println #20 = Utf8 ()V #21 = Utf8 StackMapTable #22 = Utf8 Foo.jasm #23 = Utf8 Ljava/io/PrintStream; #24 = Utf8 Code #25 = Class #26 // Foo #26 = Utf8 Foo #27 = Class #28 // java/lang/System #28 = Utf8 java/lang/System #29 = Utf8 <init> #30 = NameAndType #19:#6 // println:(Ljava/lang/String;)V { public Foo(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #4 // Method java/lang/Object."<init>":()V 4: return 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: iconst_2 //載入常量2入棧 1: istore_1 //把棧頂值儲存到區域性變數表下標為1的位置,即flag =2; 2: iload_1 //取區域性變數表中下標為1的變數壓棧 3: ifeq 14 6: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 9: ldc #1 // String Hello, Java! 11: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 14: iload_1 //取區域性變數表中下標為1的變數壓棧 15: iconst_1 //常量1入棧 16: if_icmpne 27 //比較棧頂兩個值,如果不相等,則跳轉到27 19: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 22: ldc #2 // String Hello, JVM! 24: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 27: return StackMapTable: number_of_entries = 2 frame_type = 252 /* append */ offset_delta = 14 locals = [ int ] frame_type = 12 /* same */ } SourceFile: "Foo.jasm"
附:asmtools.jar的安裝(centos 6/7)
0.先cd進入需要安裝到的目錄地址
1. Mercurial是一種輕量級分散式版本控制系統,採用Python語言實現。
yum install hg
2. 版本是2.6.2,發現不是最新版,去官方下載centos 7最新版3.9.2
wget https://www.mercurial-scm.org/release/centos7/RPMS/x86_64/mercurial-3.9.2-1.x86_64.rpm
3.升級2.6.2 到3.9.2 (centos 6 我就沒有用這一步。。)
rpm -Uvh mercurial-3.9.2-1.x86_64.rpm
4.安裝ASMTOOLS.jar
hg clone http://hg.openjdk.java.net/code-tools/asmtools/ asmtools cd asmtools/build/ yum install ant ant //編譯生成asmtools.jar
5.生成的jar位置:
[root@localhost lib]# ls asmtools.jar [root@localhost lib]# pwd /usr/local/asmtools-7.0-build/binaries/lib