今天帶來一個好玩的工具——JAD,他是幹什麼的呢,這個工具可以把java的類文件反編譯成java的源代碼,用過android反編譯的肯定玩過JD_GUI這個工具,他可以查看.jar文件裏面的類信息,雖然JAD沒有JD_GUI那麼的強勢(JAD看不了jar文件),但是,對於學java的一些執行過程來說,還是很有幫助的,關鍵是十分的有趣。
使用必備工具:JAD.exe下載鏈接,我們下載下來最好放在配置了java環境的jdk的bin目錄下面,這樣可以隨意jad的去玩,再來一個JAD的命令使用大全
ok,準備完畢,接下來帶來一個好玩的東西,也是無意之中發現的,在jdk1.7之後,switch語句已經支持使用String去作爲判斷類型了,下面就針對這個例子玩一下。
java源代碼
class A{
public static void main(String args[]){
String a="hello";
String b="";
switch(a){
case "word":
b="word";
break;
case "hello":
b="hello";
break;
}
System.out.println(b);
}
}
java代碼沒什麼,接下來我們編譯一下這個類文件,會生成A.class文件
javac A.java
然後我們輸入jad的反編譯命令,下面這句話的意思是,-P是輸出到哪裏,A.class是被反編譯的類文件,>是輸出到哪裏,B.java指定輸出的文件
jad -p A.class>B.java
然後我們打開B.java查看一下這個被反編譯後的代碼
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name: A.java
import java.io.PrintStream;
class A
{
A(){ }
public static void main(String args[])
{
String s = "hello";
String s1 = "";
String s2 = s;
byte byte0 = -1;
switch(s2.hashCode())
{
case 3655434:
if(s2.equals("word"))
byte0 = 0;
break;
case 99162322:
if(s2.equals("hello"))
byte0 = 1;
break;
}
switch(byte0)
{
case 0: // '\0'
s1 = "word";
break;
case 1: // '\001'
s1 = "hello";
break;
}
System.out.println(s1);
}
}
再來一張對比的照片
這個代碼很有特點,也讓我們知道了java是怎麼去處理switch的字符串,接下來我們分析分析。
首先我們可以看到最上部分的註釋信息,這個信息似乎是去不掉的,很有特點,哈哈。
上面還有一個import的導包,我們明明在java代碼中是沒有導過包的,怎麼這裏出現這個包呢,要知道,System.out.println這個語句中的println,是io操作的,雖然我們java文件沒有導包,因爲我們使用的是默認的lang包,所以,明白的吧。
接下來繼續,類的初始化會觸發默認的構造函數,雖然我們在java代碼中沒有寫,但是,反編譯的時候,我們卻清晰可見,構造方法A被調用過了。
接下來看main方法,這個地方是勾起我最有趣的地方,在jdk1.7之前,switch是不支持String類型的判斷的,但是在1.7後開始支持了,後來在想,他是怎麼支持字符串的呢,在編譯java代碼的時候,jvm只支持8種基本類型,沒有String的身影啊,然後通過jad編譯之後,發現了這個好玩的地方。
我們看switch判斷部分,他將字符串進行了hash計算,最終,還是通過數值進行的判斷,我們知道,同一個字符串的Hash值肯定是相同的,但是呢,反過來一樣嗎?那當然肯定不一樣,那是小概率事件,不過,通過這個編譯的源碼可以看出,還是對這些小概率做了處理,那就是在case語句中,進行equals的判斷,如果出現小概率事件了,不要緊,我們再判斷一下值是否一樣,如果這兩個都滿足了,我們就給個flag值,也就是byte0。
這編譯代碼真好玩,又來一個switch繼續判斷,哈哈,還是挺奇妙的,代碼不難,大家自己看看,反正,我覺得是非常的有趣。