今天带来一个好玩的工具——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继续判断,哈哈,还是挺奇妙的,代码不难,大家自己看看,反正,我觉得是非常的有趣。