• 一个简单的例子教会您使用javap


    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/Java_bytecode_instruction_listings

    下面对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的原创技术文章,请关注公众号"汪子熙"或者扫描下面二维码:

  • 相关阅读:
    Linux(debian)安装Redis教程
    Linux下Nginx学习笔记
    Linux常用命令
    MySQL代码片段
    MySql学习笔记
    漂亮的Web颜色
    Electron开发问题总结
    Git 教程
    Python基础
    【原创】servlet+spring+mybatis配置总结
  • 原文地址:https://www.cnblogs.com/sap-jerry/p/9812309.html
Copyright © 2020-2023  润新知