java之图解String引用类型,你真的了解她么?

1.描述:

	被真相蒙蔽,是一件很痛苦的事。
	希望读者依然本着怀疑的态度,带着求知欲到达理想彼岸。

读懂本篇文章需要会解读字节码文件,请移步:

2.代码案例(JDK8):代码–图解–分析过程

public class TopicString {
    public static void main(String[] args) {
        String s1 = "1";
        String s2 = new String("1");
        String s3 = "1" + "2" + "3";
        String s4 = "123";
        String s5 = "1" + "3" + new String("1") + "4";

        System.out.println(s1 == s2);
        System.out.println(s1.equals(s2));
        System.out.println(s3 == s4);
        System.out.println(s3.equals(s4));
    }
}

以上代码在内存中的分布图:
在这里插入图片描述

3.提出问题:

  1. 输出结果(不许偷偷的运行代码哦)
  2. s1,s2,s3,s4,s5各创建了几个对象?分析原因?

4.分析代码:

问1:如何分析以上代码?心中没有思路...没有底气!!
答1:java文件被javac编译,然后交给jvm,jvm调用机器指令和计算底层对话,并返回结果(总体探索思路)
问2:javac做了什么事?产生的结果是什么?
答2:java文件被编译后,产生一个我们熟悉的class文件(你真的熟悉class文件),那看看class信息吧

a.案例代码class文件主要操作指令信息:

  • 查看信息命令:javap -v java文件名 或者 javap -c java文件名这里笔者用java -v输出的信息。解读class文件,笔者后续会写一篇解读class信息的文章。暂时看不懂没有关系,主要部分都要图解。
F:\install-dev\java8\bin\javap.exe -v com.lsz.interview.questions.TopicString
Classfile /F:/project-my/pro-github/books/big_talk_java_performance/out/production/big_talk_java_performance/com/lsz/interview/questions/TopicString.class
  Last modified 2020-4-29; size 1186 bytes
  MD5 checksum d522389930b4243c44f3b7c836f09984
  Compiled from "TopicString.java"
public class com.lsz.interview.questions.TopicString
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #16.#40        // java/lang/Object."<init>":()V
   #2 = String             #41            // 1
   #3 = Class              #42            // java/lang/String
   #4 = Methodref          #3.#43         // java/lang/String."<init>":(Ljava/lang/String;)V
   #5 = String             #44            // 123
   #6 = Class              #45            // java/lang/StringBuilder
   #7 = Methodref          #6.#40         // java/lang/StringBuilder."<init>":()V
   #8 = String             #46            // 13
   #9 = Methodref          #6.#47         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #10 = String             #48            // 4
  #11 = Methodref          #6.#49         // java/lang/StringBuilder.toString:()Ljava/lang/String;
  #12 = Fieldref           #50.#51        // java/lang/System.out:Ljava/io/PrintStream;
  #13 = Methodref          #52.#53        // java/io/PrintStream.println:(Z)V
  #14 = Methodref          #3.#54         // java/lang/String.equals:(Ljava/lang/Object;)Z
  #15 = Class              #55            // com/lsz/interview/questions/TopicString
  #16 = Class              #56            // java/lang/Object
  #17 = Utf8               <init>
  #18 = Utf8               ()V
  #19 = Utf8               Code
  #20 = Utf8               LineNumberTable
  #21 = Utf8               LocalVariableTable
  #22 = Utf8               this
  #23 = Utf8               Lcom/lsz/interview/questions/TopicString;
  #24 = Utf8               main
  #25 = Utf8               ([Ljava/lang/String;)V
  #26 = Utf8               args
  #27 = Utf8               [Ljava/lang/String;
  #28 = Utf8               s1
  #29 = Utf8               Ljava/lang/String;
  #30 = Utf8               s2
  #31 = Utf8               s3
  #32 = Utf8               s4
  #33 = Utf8               s5
  #34 = Utf8               StackMapTable
  #35 = Class              #27            // "[Ljava/lang/String;"
  #36 = Class              #42            // java/lang/String
  #37 = Class              #57            // java/io/PrintStream
  #38 = Utf8               SourceFile
  #39 = Utf8               TopicString.java
  #40 = NameAndType        #17:#18        // "<init>":()V
  #41 = Utf8               1
  #42 = Utf8               java/lang/String
  #43 = NameAndType        #17:#58        // "<init>":(Ljava/lang/String;)V
  #44 = Utf8               123
  #45 = Utf8               java/lang/StringBuilder
  #46 = Utf8               13
  #47 = NameAndType        #59:#60        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #48 = Utf8               4
  #49 = NameAndType        #61:#62        // toString:()Ljava/lang/String;
  #50 = Class              #63            // java/lang/System
  #51 = NameAndType        #64:#65        // out:Ljava/io/PrintStream;
  #52 = Class              #57            // java/io/PrintStream
  #53 = NameAndType        #66:#67        // println:(Z)V
  #54 = NameAndType        #68:#69        // equals:(Ljava/lang/Object;)Z
  #55 = Utf8               com/lsz/interview/questions/TopicString
  #56 = Utf8               java/lang/Object
  #57 = Utf8               java/io/PrintStream
  #58 = Utf8               (Ljava/lang/String;)V
  #59 = Utf8               append
  #60 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #61 = Utf8               toString
  #62 = Utf8               ()Ljava/lang/String;
  #63 = Utf8               java/lang/System
  #64 = Utf8               out
  #65 = Utf8               Ljava/io/PrintStream;
  #66 = Utf8               println
  #67 = Utf8               (Z)V
  #68 = Utf8               equals
  #69 = Utf8               (Ljava/lang/Object;)Z
{
  public com.lsz.interview.questions.TopicString();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 12: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/lsz/interview/questions/TopicString;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=6, args_size=1
         0: ldc           #2                  // String 1
         2: astore_1
         3: new           #3                  // class java/lang/String
         6: dup
         7: ldc           #2                  // String 1
         9: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
        12: astore_2
        13: ldc           #5                  // String 123
        15: astore_3
        16: ldc           #5                  // String 123
        18: astore        4
        20: new           #6                  // class java/lang/StringBuilder
        23: dup
        24: invokespecial #7                  // Method java/lang/StringBuilder."<init>":()V
        27: ldc           #8                  // String 13
        29: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        32: new           #3                  // class java/lang/String
        35: dup
        36: ldc           #2                  // String 1
        38: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
        41: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        44: ldc           #10                 // String 4
        46: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        49: invokevirtual #11                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        52: astore        5
        54: getstatic     #12                 // Field java/lang/System.out:Ljava/io/PrintStream;
        57: aload_1
        58: aload_2
        59: if_acmpne     66
        62: iconst_1
        63: goto          67
        66: iconst_0
        67: invokevirtual #13                 // Method java/io/PrintStream.println:(Z)V
        70: getstatic     #12                 // Field java/lang/System.out:Ljava/io/PrintStream;
        73: aload_1
        74: aload_2
        75: invokevirtual #14                 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
        78: invokevirtual #13                 // Method java/io/PrintStream.println:(Z)V
        81: getstatic     #12                 // Field java/lang/System.out:Ljava/io/PrintStream;
        84: aload_3
        85: aload         4
        87: if_acmpne     94
        90: iconst_1
        91: goto          95
        94: iconst_0
        95: invokevirtual #13                 // Method java/io/PrintStream.println:(Z)V
        98: getstatic     #12                 // Field java/lang/System.out:Ljava/io/PrintStream;
       101: aload_3
       102: aload         4
       104: invokevirtual #14                 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
       107: invokevirtual #13                 // Method java/io/PrintStream.println:(Z)V
       110: return
      LineNumberTable:
        line 14: 0
        line 15: 3
        line 16: 13
        line 17: 16
        line 18: 20
        line 20: 54
        line 21: 70
        line 22: 81
        line 23: 98
        line 24: 110
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0     111     0  args   [Ljava/lang/String;
            3     108     1    s1   Ljava/lang/String;
           13      98     2    s2   Ljava/lang/String;
           16      95     3    s3   Ljava/lang/String;
           20      91     4    s4   Ljava/lang/String;
           54      57     5    s5   Ljava/lang/String;
      StackMapTable: number_of_entries = 4
        frame_type = 255 /* full_frame */
          offset_delta = 66
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/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, class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream, int ]
        frame_type = 90 /* same_locals_1_stack_item */
          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, class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream, int ]
}
SourceFile: "TopicString.java"

}
SourceFile: "TopicString.java"

分析得出初始图
在这里插入图片描述

b.main方法中的指令解读

编译之后,class文件只是一个普通本地文件,存在C盘或者D盘而已,和txt、excel、word一样都是一个"静态文件"。因此java文件,
接下会进行class类加载,将静态文件class加载到内存中,jvm读取指令然后操作指令
# 表示:多少号,例如:#2表示常量池中的2号位置

常量池数据类型:boolean、byte、short、int、char、float、reference或者returnAddress

第一组指令:String s1 = "1";
  • 指令:ldc 将常量池中的数据类型推入栈顶
  • 指令:astore_1 将栈顶类型值保存到局部变量表slot=1位置
    // 执行指令
    0: ldc           #2                  // String 1
    2: astore_1
    
    // 对应源码
    String s1 = "1";
    
    将常量池中 #2 位置的值推到栈顶,从反编译得知:#2 最终的结果是:String 1,但是常量池中的数据类型没有String啊,是的!因此这里保存的是引用地址,也就是常量池数据类型中的reference类型,出栈。执行指令后:
    在这里插入图片描述
第二组指令:String s2 = new String("1");
  • 指令:new创建新的对象实例,并将引用(reference)地址压入栈

  • 指令:dup复制栈顶数值,并将复制值压入栈顶

  • 指令:ldc 将常量池中的数据类型推入栈顶

  • 指令:invokespecial 编译时调用方法,初始化对象值,出栈。

  • 指令:astore_2 将栈顶类型保存到局部变量表slot=2的位置,出栈。

    // 执行指令
    3: new           #3                  // class java/lang/String
    6: dup
    7: ldc           #2                  // String 1
    9: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
    12: astore_2
    
    // 对应的源码
    String s2 = new String("1");
    

    1.执行3: new #3指令和dup指令
    在这里插入图片描述
    2.执行7: ldc #2指令和9: invokespecial #4指令:
    在这里插入图片描述

    3.执行12: astore_2指令:
    在这里插入图片描述

第三组指令:String s3 = "1" + "2" + "3";String s4 = "123";
  • 类比上边的参数解读

    // 执行指令
    13: ldc           #5                  // String 123
    15: astore_3
    16: ldc           #5                  // String 123
    18: astore        4
    
    // 对应的源码
    String s3 = "1" + "2" + "3";
    String s4 = "123";
    

    在这里插入图片描述
    问:为什么s3和s4的地址一样?
    答:因为java编译器在编译的时候优化了,这个概念称为:常量折叠,感兴趣的童鞋可以去看javac源码

第四组指令:String s5 = "1" + "3" + new String("1") + "4";
  • 类比上边的参数解读

    // 执行指令
        20: new           #6                  // class java/lang/StringBuilder
        23: dup
        24: invokespecial #7                  // Method java/lang/StringBuilder."<init>":()V
        27: ldc           #8                  // String 13
        29: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        32: new           #3                  // class java/lang/String
        35: dup
        36: ldc           #2                  // String 1
        38: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
        41: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        44: ldc           #10                 // String 4
        46: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        49: invokevirtual #11                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        52: astore        5
    
    // 对应的源码
    String s5 = "1" + "3" + new String("1") + "4";
    

    在这里插入图片描述

再往下读:注意的指令
aload_1:从局部变量表slot=1位置的数据类型推到栈顶
aload_2:从局部变量表slot=2位置的数据类型推到栈顶
然后进行比较,这样看就知道== 和 equals的区别,答案就在眼前

5.得出总的结果分析图:

在这里插入图片描述

5.留给读者的问题:

  • 输出的结果?(现在可以运行看看了)
  • s1,s2,s3,s4,s5各创建了几个对象?
  • ...有答案了么?

6.附加信息(验证字符串常量池的位置):

public class StringPoolPosition {
    static String base = "string";
    public static void main(String[] args) {
        // 打印java版本
        System.out.println(System.getProperty("java.version"));
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            base = base.concat(base);
            // 插入到字符常量池中
            base.intern();
        }
    }
}

// 结果分析
1.8.0_181
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at java.util.Arrays.copyOf(Arrays.java:3332)
	at java.lang.String.concat(String.java:2032)
	at com.lsz.stack.StringPoolPosition.main(StringPoolPosition.java:16)

// 字符串常量池,在堆中

7.结语:

切勿人云亦云,不轻易被真相蒙蔽。
有问题可以私信或者评论下方留言哦!! 希望对你有帮助!!!

原创文章 19 获赞 109 访问量 19万+

猜你喜欢

转载自blog.csdn.net/qq_40670946/article/details/105816148