java 【字节码二】面试小题分析

环境

java:1.7

例子一

package test.byteCode;

public class StringBuilderTest {

    public static void main(String[] args) {

        String v1 = "abc";
        String v2 = "a";
        String v3 = "bc";

        System.out.println(v1 == (v2+v3));
    }
}

答案是false

字节码源码

Classfile /D:/sts/workspace/execlTest/build/classes/test/byteCode/StringBuilderTest.class
  Last modified 2018-5-24; size 1013 bytes
  MD5 checksum 803eb113d48257bb3b567c4fe2fad0f2
  Compiled from "StringBuilderTest.java"
public class test.byteCode.StringBuilderTest
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Class              #2             // test/byteCode/StringBuilderTest
   #2 = Utf8               test/byteCode/StringBuilderTest
   #3 = Class              #4             // java/lang/Object
   #4 = Utf8               java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = Utf8               Code
   #8 = Methodref          #3.#9          // java/lang/Object."<init>":()V
   #9 = NameAndType        #5:#6          // "<init>":()V
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               Ltest/byteCode/StringBuilderTest;
  #14 = Utf8               main
  #15 = Utf8               ([Ljava/lang/String;)V
  #16 = String             #17            // abc
  #17 = Utf8               abc
  #18 = String             #19            // a
  #19 = Utf8               a
  #20 = String             #21            // bc
  #21 = Utf8               bc
  #22 = Fieldref           #23.#25        // java/lang/System.out:Ljava/io/PrintStream;
  #23 = Class              #24            // java/lang/System
  #24 = Utf8               java/lang/System
  #25 = NameAndType        #26:#27        // out:Ljava/io/PrintStream;
  #26 = Utf8               out
  #27 = Utf8               Ljava/io/PrintStream;
  #28 = Class              #29            // java/lang/StringBuilder
  #29 = Utf8               java/lang/StringBuilder
  #30 = Methodref          #31.#33        // java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
  #31 = Class              #32            // java/lang/String
  #32 = Utf8               java/lang/String
  #33 = NameAndType        #34:#35        // valueOf:(Ljava/lang/Object;)Ljava/lang/String;
  #34 = Utf8               valueOf
  #35 = Utf8               (Ljava/lang/Object;)Ljava/lang/String;
  #36 = Methodref          #28.#37        // java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
  #37 = NameAndType        #5:#38         // "<init>":(Ljava/lang/String;)V
  #38 = Utf8               (Ljava/lang/String;)V
  #39 = Methodref          #28.#40        // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #40 = NameAndType        #41:#42        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #41 = Utf8               append
  #42 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #43 = Methodref          #28.#44        // java/lang/StringBuilder.toString:()Ljava/lang/String;
  #44 = NameAndType        #45:#46        // toString:()Ljava/lang/String;
  #45 = Utf8               toString
  #46 = Utf8               ()Ljava/lang/String;
  #47 = Methodref          #48.#50        // java/io/PrintStream.println:(Z)V
  #48 = Class              #49            // java/io/PrintStream
  #49 = Utf8               java/io/PrintStream
  #50 = NameAndType        #51:#52        // println:(Z)V
  #51 = Utf8               println
  #52 = Utf8               (Z)V
  #53 = Utf8               args
  #54 = Utf8               [Ljava/lang/String;
  #55 = Utf8               v1
  #56 = Utf8               Ljava/lang/String;
  #57 = Utf8               v2
  #58 = Utf8               v3
  #59 = Utf8               StackMapTable
  #60 = Class              #54            // "[Ljava/lang/String;"
  #61 = Utf8               SourceFile
  #62 = Utf8               StringBuilderTest.java
{
  public test.byteCode.StringBuilderTest();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #8                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Ltest/byteCode/StringBuilderTest;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=5, locals=4, args_size=1
         0: ldc           #16                 // String abc
         2: astore_1
         3: ldc           #18                 // String a
         5: astore_2
         6: ldc           #20                 // String bc
         8: astore_3
         9: getstatic     #22                 // Field java/lang/System.out:Ljava/io/PrintStream;
        12: aload_1
        13: new           #28                 // class java/lang/StringBuilder
        16: dup
        17: aload_2
        18: invokestatic  #30                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
        21: invokespecial #36                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
        24: aload_3
        25: invokevirtual #39                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        28: invokevirtual #43                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        31: if_acmpne     38
        34: iconst_1
        35: goto          39
        38: iconst_0
        39: invokevirtual #47                 // Method java/io/PrintStream.println:(Z)V
        42: return
      LineNumberTable:
        line 13: 0
        line 14: 3
        line 15: 6
        line 17: 9
        line 18: 42
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      43     0  args   [Ljava/lang/String;
            3      40     1    v1   Ljava/lang/String;
            6      37     2    v2   Ljava/lang/String;
            9      34     3    v3   Ljava/lang/String;
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 38
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream ]
        frame_type = 255 /* full_frame */
          offset_delta = 0
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream, int ]
}
SourceFile: "StringBuilderTest.java"

从字节码的角度分析:

说明:
1、 undefined表示该变量没有初始化
2、| 左边是局部变量,0表示第一个局部变量,依次类推,| 右边是栈底到栈顶元素
3、赋值给slot为1的元素 可以理解为 赋值给 slot 为1 的变量

[0:undefined,1:un,2:un:, 3:un | "abc"] -- ldc 加载常量"abc"

[0:undefined,1:un,2:un:, 3:un | "abc"] -- astore_1 将栈顶元素弹出赋值给slot为1的元素
   [0:undefined,1:"abc",2:un:, 3:un | ]

[0:undefined,1:"abc",2:un:, 3:un | "a"] -- ldc 加载常量"a"

[0:undefined,1:"abc",2:un:, 3:un | "a"] -- astore_2 将栈顶元素弹出,赋值给slot为2的元素
    [0:undefined,1:"abc",2:"a":, 3:un | ]

[0:undefined,1:"abc",2:"a":, 3:un | "bc" ] -- ldc 加载常量"bc"
    [0:undefined,1:"abc",2:"a":, 3:"bc" |  ] -- astore_3 将栈顶元素弹出,赋值给slot为3的元素

[0:undefined,1:"abc",2:"a":, 3:"bc" |  ]  getstatic -- 加载静态字段System.out,并压入栈顶
    [0:undefined,1:"abc",2:"a":, 3:"bc" | "System.out" ] 其是PrintStream类型的

[0:undefined,1:"abc",2:"a":, 3:"bc" | "System.out" ] -- aload_1 将slot为1的变量的值压入栈顶
    [0:undefined,1:"abc",2:"a":, 3:"bc" | "System.out", "abc"]

[0:undefined,1:"abc",2:"a":, 3:"bc" | "System.out", "abc"] -- new 创建一个对象并压入栈顶
    [0:undefined,1:"abc",2:"a":, 3:"bc" | "System.out", "abc", "StringBuilder"]

[0:undefined,1:"abc",2:"a":, 3:"bc" | "System.out", "abc", "StringBuilder"] -- dup 复制栈顶元素并压入栈顶
    [0:undefined,1:"abc",2:"a":, 3:"bc" | "System.out", "abc", "StringBuilder", "StringBuilder"]

[0:undefined,1:"abc",2:"a":, 3:"bc" | "System.out", "abc", "StringBuilder", "StringBuilder"] -- aload_2 将slot为2的变量值压入栈顶
    [0:undefined,1:"abc",2:"a":, 3:"bc" | "System.out", "abc", "StringBuilder", "StringBuilder", "a"]

[0:undefined,1:"abc",2:"a":, 3:"bc" | "System.out", "abc", "StringBuilder", "StringBuilder", "a"] --invokestatic 将栈顶元素"a"弹出并作

为参数传给String.valueOf()方法,并将返回值压入栈顶
    [0:undefined,1:"abc",2:"a":, 3:"bc" | "System.out", "abc", "StringBuilder", "StringBuilder", "a"]

[0:undefined,1:"abc",2:"a":, 3:"bc" | "System.out", "abc", "StringBuilder", "StringBuilder", "a"] --invokespecial 弹出栈顶两个元素并将

参数"a"传入给StringBuilder构造器
    [0:undefined,1:"abc",2:"a":, 3:"bc" | "System.out", "abc", "StringBuilder"]

[0:undefined,1:"abc",2:"a":, 3:"bc" | "System.out", "abc", "StringBuilder" ] --aload_3 将slot为3的变量的值压入栈顶
    [0:undefined,1:"abc",2:"a":, 3:"bc" | "System.out", "abc", "StringBuilder", "bc"]

[0:undefined,1:"abc",2:"a":, 3:"bc" | "System.out", "abc", "StringBuilder", "bc"] --invokevirtual 将栈顶的两个元素弹出并将bc作为参数传

给append方法,a.append("bc") 返回"abc"压入栈顶
    [0:undefined,1:"abc",2:"a":, 3:"bc" | "System.out", "abc", "abc"]

[0:undefined,1:"abc",2:"a":, 3:"bc" | "System.out", "abc", "abc"] --invokevirtual将栈顶变量弹出执行"abc".toString(),并将返回值,压入栈

顶
    [0:undefined,1:"abc",2:"a":, 3:"bc" | "System.out", "abc", "abc"]

[0:undefined,1:"abc",2:"a":, 3:"bc" | "System.out", "abc", "abc"] --if_acmpne 将栈顶两个引用型变量弹出进行比较,如果不等跳转至第38(字

节码中编号)指令 很显然 由于后者是使用append方法进行拼接而来,所以不相等,跳转38行
    [0:undefined,1:"abc",2:"a":, 3:"bc" | "System.out"]

[0:undefined,1:"abc",2:"a":, 3:"bc" | "System.out""0"] --iconst_0加载int型常量0

[0:undefined,1:"abc",2:"a":, 3:"bc" | "System.out""0"] -- 弹出栈顶两个元素,其中"0"作为参数,执行"System.out.printlt(0)"方法

从上面可以看出,其是通过append()方法来拼接字符串的,所以其返回false.

例子二

源码

package test.byteCode;

public class StringBuilderTest {

    public static void main(String[] args) {        
        String v1 = "abc";
        final String v2 = "a";
        final String v3 = "bc";

        System.out.println(v1 == (v2+v3));
    }
}

结果为true

字节码

Classfile /D:/sts/workspace/execlTest/build/classes/test/byteCode/StringBuilderTest.class
  Last modified 2018-5-23; size 762 bytes
  MD5 checksum 62874fe9ef2eda28b00fb9cb1e5a4501
  Compiled from "StringBuilderTest.java"
public class test.byteCode.StringBuilderTest
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Class              #2             // test/byteCode/StringBuilderTest
   #2 = Utf8               test/byteCode/StringBuilderTest
   #3 = Class              #4             // java/lang/Object
   #4 = Utf8               java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = Utf8               Code
   #8 = Methodref          #3.#9          // java/lang/Object."<init>":()V
   #9 = NameAndType        #5:#6          // "<init>":()V
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               Ltest/byteCode/StringBuilderTest;
  #14 = Utf8               main
  #15 = Utf8               ([Ljava/lang/String;)V
  #16 = String             #17            // abc
  #17 = Utf8               abc
  #18 = String             #19            // a
  #19 = Utf8               a
  #20 = String             #21            // bc
  #21 = Utf8               bc
  #22 = Fieldref           #23.#25        // java/lang/System.out:Ljava/io/PrintStream;
  #23 = Class              #24            // java/lang/System
  #24 = Utf8               java/lang/System
  #25 = NameAndType        #26:#27        // out:Ljava/io/PrintStream;
  #26 = Utf8               out
  #27 = Utf8               Ljava/io/PrintStream;
  #28 = Methodref          #29.#31        // java/io/PrintStream.println:(Z)V
  #29 = Class              #30            // java/io/PrintStream
  #30 = Utf8               java/io/PrintStream
  #31 = NameAndType        #32:#33        // println:(Z)V
  #32 = Utf8               println
  #33 = Utf8               (Z)V
  #34 = Utf8               args
  #35 = Utf8               [Ljava/lang/String;
  #36 = Utf8               v1
  #37 = Utf8               Ljava/lang/String;
  #38 = Utf8               v2
  #39 = Utf8               v3
  #40 = Utf8               StackMapTable
  #41 = Class              #35            // "[Ljava/lang/String;"
  #42 = Class              #43            // java/lang/String
  #43 = Utf8               java/lang/String
  #44 = Utf8               SourceFile
  #45 = Utf8               StringBuilderTest.java
{
  public test.byteCode.StringBuilderTest();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #8                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Ltest/byteCode/StringBuilderTest;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=4, args_size=1
         0: ldc           #16                 // String abc
         2: astore_1
         3: ldc           #18                 // String a
         5: astore_2
         6: ldc           #20                 // String bc
         8: astore_3
         9: getstatic     #22                 // Field java/lang/System.out:Ljava/io/PrintStream;
        12: aload_1
        13: ldc           #16                 // String abc
        15: if_acmpne     22
        18: iconst_1
        19: goto          23
        22: iconst_0
        23: invokevirtual #28                 // Method java/io/PrintStream.println:(Z)V
        26: return
      LineNumberTable:
        line 13: 0
        line 14: 3
        line 15: 6
        line 17: 9
        line 18: 26
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      27     0  args   [Ljava/lang/String;
            3      24     1    v1   Ljava/lang/String;
            6      21     2    v2   Ljava/lang/String;
            9      18     3    v3   Ljava/lang/String;
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 22
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream ]
        frame_type = 255 /* full_frame */
          offset_delta = 0
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream, int ]
}
SourceFile: "StringBuilderTest.java"

分析字节码

[0:un, 1:un, 2:un, 3:un | "abc"] --ldc 加载常量"abc"

[0:un, 1:un, 2:un, 3:un | "abc"] -- astore_1 将栈顶元素弹出赋值给slot为1的变量
    [0:un, 1:un, 2:"abc", 3:un | ]

[0:un, 1:un, 2:"abc", 3:un | ] -- ldc 加载常量"a"
    [0:un, 1:un, 2:"abc", 3:un | "a"]

[0:un, 1:un, 2:"abc", 3:un | "a"] --astore_2 将栈顶元素弹出赋值给slot为2的变量
    [0:un, 1:un, 2:"abc", 3:un | ]

[0:un, 1:un, 2:"abc", 3:un | ] -- ldc 加载常量"bc"
    [0:un, 1:un, 2:"abc", 3:un | "bc"]

[0:un, 1:un, 2:"abc", 3:un | "bc"] --astore_3 将栈顶元素弹出并赋值给slot为3的变量
    [0:un, 1:un, 2:"abc", 3:"bc" | ]

[0:un, 1:un, 2:"abc", 3:"bc" | ] --getstatic 加载PrintStream类的静态字段System.out
    [0:un, 1:un, 2:"abc", 3:"bc" | "System.out"]

[0:un, 1:un, 2:"abc", 3:"bc" | "System.out"] --aload_1 将slot为1的变量的值压入栈顶
    [0:un, 1:un, 2:"abc", 3:"bc" | "System.out", "abc"]

[0:un, 1:un, 2:"abc", 3:"bc" | "System.out", "abc", "abc"] -- ldc 加载常量abc

[0:un, 1:un, 2:"abc", 3:"bc" | "System.out", "abc", "abc"] -- if_acmpne 弹出栈顶两个引用型元素并进行比较,若不等继续往下执行,否则就跳转到字节码序号为22行的指令
    [0:un, 1:un, 2:"abc", 3:"bc" | "System.out"]

其实就是是返回0还是返回1的事情。

[0:un, 1:un, 2:"abc", 3:"bc" | ]

因为其没有去执行append()方法来拼接abc。而是编译的时候直接就拼接好了,所以在使用时,是直接加载常量abc字符串。因此返回的是true

总结

虽然很简单,但可以看出final使得jvm少干了很多事情!

猜你喜欢

转载自blog.csdn.net/u013066244/article/details/80431534