摘要:从字节码的分析可以观察到一个有趣的现象,再次看看我们的语句。这张表里每行的后面的数字代表源代码的序号,冒号后面的数字代表字节码里每行指令的序号。维护了源代码同字节指令的映射关系,确保了代码调试的顺利进行。
javap是JDK自带的工具:
这篇文章使用下面这段简单的Java代码作为例子进行讲解。
class Outer { Nested nested; Nested getNested() { return nested; } } class Nested { Inner inner; Inner getInner() { return inner; } } class Inner { String foo; String getFoo() { return foo; } } public class NullableTest { public static Outer getInitializedOuter(){ Outer outer = new Outer(); outer.nested = new Nested(); outer.nested.inner = new Inner(); outer.nested.inner.foo = "Jerry"; return outer; } /* null pointer exception private static void way0(){ Outer outer = new Outer(); System.out.println(outer.nested.inner.foo); }*/ public static void way1(){ Outer outer = getInitializedOuter(); if (outer != null && outer.nested != null && outer.nested.inner != null) { System.out.println(outer.nested.inner.foo); } } public static void main(String[] args) { //way0(); way1(); } }
使用下面的命令行对NullableTest进行反编译,以java编译器生成的字节码:
javap -v NullableTest >c:code1.txt
查看方法way1()对应的字节码:
下面这个wiki包含了java字节码里每个指令的具体说明:
https://en.wikipedia.org/wiki...
下面对NullableTest反编译得到的字节码做一些说明:
0: invokestatic #42 // Method getInitializedOuter:()Ljava8/Outer;
代表静态方法getInitializedOuter的调用, Ljava8/Outer意思是该方法的返回类型是Outer
3: astore_0
将上述静态方法调用返回的outer引用存储到局部变量中,局部变量的id为0.
4: aload_0
因为在我前面的Java源代码中,我将静态方法返回的对象引用同null做了比较,因此使用指令aload_0将存储在代号为0的局部变量中的对象引用重新加载到栈上,此后才能和null做比较。
5: ifnull 41
这就是我在Java源代码里书写的IF分支。如果IF分支里检测的outer引用为null,则直接返回了。体现在字节码就是,如果ifnull为true,则跳转到第41行字节码,即直接返回。
如果ifnull不为true,则继续执行下去。又将outer引用加载到栈上。
从字节码的分析可以观察到一个有趣的现象,再次看看我们的IF语句。
Java编译时,编译器实际将其转换成了下面的写法:
if (outer == null ) return; if( outer.nested == null ) return; if( outer.nested.inner == null) return; System.out.println(outer.nested.inner.foo);
这个事实可以通过下图得到确认。
javap生成的字节码里的LineNumberTable也很有用。这张表里每行的line后面的数字代表Java源代码的序号,line XX冒号后面的数字代表字节码里每行指令的序号。看看下图中Java源代码和对应的字节指令在LineNumberTable中的映射关系。
LineNumberTable维护了Java源代码同字节指令的映射关系,确保了Java代码调试的顺利进行。
要获取更多Jerry的原创技术文章,请关注公众号"汪子熙"或者扫描下面二维码:
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/71747.html
摘要:我下图代码第五行和第九行分别定义了一个整型变量和一个整型常量程序员都知道两者的区别。下面我们就用将文件反编译出来然后深入研究里整型变量和整型常量的区别。 我下图代码第五行和第九行分别定义了一个整型变量和一个整型常量: static final int number1 = 512; static int number3 = 545; Java程序员都知道两者的区别。 showImg(ht...
摘要:使用命令行将包含了这行代码的类反编译查看其字节码我们看到字符串被编译器加到了常量池里。代码被翻译成了下面两句字节码首先底层的原生方法被调用,生成的内部存储实现。做一个字符串拼接的操作。 我们看这样一行简单的字符串赋值操作的Java代码。 String a = i042416; 使用命令行将包含了这行代码的Java类反编译查看其字节码: javap -v constant.Constan...
摘要:但是有一个的指令,可以把字节码翻译成人类能看懂的东西。是文件分解器,可以反编译即对编译的文件进行反编译,也可以查看编译器生成的字节码。现在有一个类,定义入下先用编译成字节码,再使用进行反编译。 概要 Java工程师面试官偏爱的问题之一,就是abc和 new String(abc)的区别是什么?回答的比较好的会带出Java堆,栈,常量池,引用等概念。但今天不止如此,我们从指令的角度,去看...
摘要:反汇编器与反编译器不同,反编译器的目标是高级语言而非汇编语言。反汇编器的反汇编输出通常格式化为适合人类阅读,而非用作汇编器的输入源,因此它主要是一个逆向工程工具。本文章参考了通过命令分析汇编指令反汇编器 问题描述 写这篇文章是为了记录我这几天遇到的一个疑惑,并且顺藤摸瓜的学习一下javap命令。遇到的疑惑是这样的:我在看使用枚举类型实现单列模式的博客时,发现一些博客中写到的枚举类型的反...
阅读 2256·2023-04-26 00:46
阅读 562·2023-04-25 21:36
阅读 687·2021-11-24 10:19
阅读 2232·2021-11-23 09:51
阅读 981·2021-10-21 09:39
阅读 775·2021-09-22 10:02
阅读 1635·2021-09-03 10:29
阅读 2603·2019-08-30 15:53