通过查看字节码来学习Java代码

最近学习java的一些知识, 通过学习编译后的字节码可以更好的了解java的编译.

1. 安装查看字节码的插件

在eclipse上直接通过Eclipse Marketplace安装, 直接搜索bytecode就可以看到插件

最后通过Window-> Show View -> Other -> Java -> Bytecode打开查看字节码

image

在IntelliJ IDEA上也通过安装插件方式

image

IntelliJ IDEAL安装好插件后, 通过下面方法查看选中文件的字节码.

image


2. 通过字节码学习java字符串的操作

下面写了一段简单的字符操作, 最后判断两个字符串是否相同. 最后输出的答案是false、true、true

image

下面是上面代码生成的字节码文件(红色字体是我添加的注释):

// access flags 0x9
  public static main([Ljava/lang/String;)V
   L0
    LINENUMBER 5 L0
    LDC "abc"    //第一个字符串常量
    ASTORE 1
   L1
    LINENUMBER 6 L1
    NEW java/lang/StringBuilder
    DUP
    ALOAD 1
    INVOKESTATIC java/lang/String.valueOf(Ljava/lang/Object;)Ljava/lang/String;
    INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V
    LDC "def"    // 在连接字符串def的时候,相当于操作 String test2 = new StringBuilder().append(test1).append("def").toString();
    INVOKEVIRTUAL java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String;
    ASTORE 2
   L2
    LINENUMBER 7 L2
    LDC "abcdef"   //第三个字符串常量
    ASTORE 3
   L3
    LINENUMBER 8 L3
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 2
    ALOAD 3
    IF_ACMPNE L4
    ICONST_1
    GOTO L5
   L4

   FRAME FULL [[Ljava/lang/String; java/lang/String java/lang/String java/lang/String] [java/io/PrintStream]
    ICONST_0
   L5
   FRAME FULL [[Ljava/lang/String; java/lang/String java/lang/String java/lang/String] [java/io/PrintStream I]
    INVOKEVIRTUAL java/io/PrintStream.println(Z)V
   L6
    LINENUMBER 9 L6
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 2
    ALOAD 3
    INVOKEVIRTUAL java/lang/String.equals(Ljava/lang/Object;)Z
    INVOKEVIRTUAL java/io/PrintStream.println(Z)V
   L7
    LINENUMBER 10 L7
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 2
    INVOKEVIRTUAL java/lang/String.intern()Ljava/lang/String;
    ALOAD 3
    IF_ACMPNE L8
    ICONST_1
    GOTO L9
   L8
   FRAME SAME1 java/io/PrintStream
    ICONST_0
   L9
   FRAME FULL [[Ljava/lang/String; java/lang/String java/lang/String java/lang/String] [java/io/PrintStream I]
    INVOKEVIRTUAL java/io/PrintStream.println(Z)V
   L10
    LINENUMBER 11 L10
    RETURN
   L11
    LOCALVARIABLE args [Ljava/lang/String; L0 L11 0
    LOCALVARIABLE test1 Ljava/lang/String; L1 L11 1
    LOCALVARIABLE test2 Ljava/lang/String; L2 L11 2
    LOCALVARIABLE test3 Ljava/lang/String; L3 L11 3
    MAXSTACK = 3
    MAXLOCALS = 4

通过以上的分析可以知道, 在创建test2字符串的时候, 是通过下面方式获取的.

String test2 = new StringBuilder().append(test1).append(“def”).toString();

这样的话字符串test2是放在堆里的, 而test1和test3是放在字符串的常量池当中的.

(这里涉及到JVM虚拟机几个大部分: 方法区、Java虚拟机栈、本地方法栈、堆、程序寄数器、字符串常量池)

这样的话,

test2 == test3 是不相等, 返回false;

test2.equals(test3)时比较值 是否相等;这里是相等, 返回true;

test2.intern()是把test2放入到字符串常量池中, 则test2.intern() == test3 是相同的, 返回true;


个人学习java知识总结:

在学习java的时候, 有时候只知道这样写是正确的, 但不知道jvm是怎么做到的.

知道是这么回事, 但是不知道为什么会是这么回事.

所以在以后学习java知识的时候, 希望自己多深入了解一下, 不能只知道表面现象, 要看到本质.

原文地址:https://www.cnblogs.com/xumBlog/p/12209893.html