这是极客时间《深入拆解Java虚拟机》第二篇的作业。
首先老师给了一段代码:
public class Foo {
static boolean boolValue;
public static void main(String[] args) {
boolValue = true; // 将这个 true 替换为 2 或者 3,再看看打印结果
if (boolValue) System.out.println("Hello, Java!");
if (boolValue == true) System.out.println("Hello, JVM!");
}
}
我们将其进行编译并使用asmtools修改字节码。
首先将true修改为2
代码如下:
public static Method main:"([Ljava/lang/String;)V"
stack 2 locals 1
{
iconst_1;
putstatic Field boolValue:"Z";
getstatic Field boolValue:"Z";
ifeq L18;
getstatic Field java/lang/System.out:"Ljava/io/PrintStream;";
ldc String "Hello, Java!";
invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/String;)V";
L18: stack_frame_type same;
getstatic Field boolValue:"Z";
iconst_1;
if_icmpne L33;
getstatic Field java/lang/System.out:"Ljava/io/PrintStream;";
ldc String "Hello, JVM!";
invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/String;)V";
L33: stack_frame_type same;
return;
}
} // end Class Foo
我们只要将Method main中的第一个iconst_1修改为iconst_2就可以了。修改后代码如下
public static Method main:"([Ljava/lang/String;)V"
stack 2 locals 1
{
iconst_2;
putstatic Field boolValue:"Z";
getstatic Field boolValue:"Z";
ifeq L18;
getstatic Field java/lang/System.out:"Ljava/io/PrintStream;";
ldc String "Hello, Java!";
invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/String;)V";
L18: stack_frame_type same;
getstatic Field boolValue:"Z";
iconst_1;
if_icmpne L33;
getstatic Field java/lang/System.out:"Ljava/io/PrintStream;";
ldc String "Hello, JVM!";
invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/String;)V";
L33: stack_frame_type same;
return;
}
} // end Class Foo
然后我们通过asmtools进行编译成.class文件。
java -jar asmtools.jar jasm Foo.jasm
我们通过反编译程序看一下代码,更加直观我们修改了什么。
/*
* Decompiled with CFR 0.151.
*/
public class Foo {
static boolean boolValue;
public static void main(String[] stringArray) {
boolValue = 2;
if (boolValue) {
System.out.println("Hello, Java!");
}
if (boolValue) {
System.out.println("Hello, JVM!");
}
}
}
明显的发现,我们是将boolValue修改为了2.
然后运行.class文件
java Foo
咦?为什么什么都没有输出呢?
我们再根据提示把iconst_1修改为iconst_3
public static Method main:"([Ljava/lang/String;)V"
stack 2 locals 1
{
iconst_3;
putstatic Field boolValue:"Z";
getstatic Field boolValue:"Z";
ifeq L18;
getstatic Field java/lang/System.out:"Ljava/io/PrintStream;";
ldc String "Hello, Java!";
invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/String;)V";
L18: stack_frame_type same;
getstatic Field boolValue:"Z";
iconst_1;
if_icmpne L33;
getstatic Field java/lang/System.out:"Ljava/io/PrintStream;";
ldc String "Hello, JVM!";
invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/String;)V";
L33: stack_frame_type same;
return;
}
} // end Class Foo
然后同上生成.class文件。同样的我们反编译一下.class来直观感受下修改了什么。
/*
* Decompiled with CFR 0.151.
*/
public class Foo {
static boolean boolValue;
public static void main(String[] stringArray) {
boolValue = 3;
if (boolValue) {
System.out.println("Hello, Java!");
}
if (boolValue) {
System.out.println("Hello, JVM!");
}
}
}
就是把boolValue修改为了3而已。
然后我们运行java Foo
什么!这次怎么有结果了
原来在底层Java虚拟机将boolean通过byte存储,但是Java虚拟机为了保证boolean数值的正确性,将boolean只保存最后一位。2的最后一位为0,所以将boolValue修改为2的时候什么也不输出。而3的最后一位为1,所以都输出了。