用字节码分析string和String new 以及String intern原理

本文在本人2个博客https://blog.csdn.net/21aspnethttps://blog.csdn.net/unix21unix21同步发布

先上代码:

public class TestApp {

    public static void main(String[] args) {
        
        String s1 = "abc";
        String s2 = new String("abc");
        String s3 = new String("abc");

        System.out.println(s2 == s1.intern());
        System.out.println(s2 == s3.intern());
        System.out.println(s1 == s3.intern());
        System.out.println(s3 == s3.intern());

        String s4 = "abcd";
        String s5 = new String("abcde");
        System.out.println(s4);
        System.out.println(s5.intern());
    }
}

输出:

false
false
true
false
abcd
abcde

第一个知识点--String.intern:

参考:Java-String.intern的深入研究

Returns a canonical representation for the string object. A pool of strings, initially empty, is maintained privately by the class String. When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned. It follows that for any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true. All literal strings and string-valued constant expressions are interned. String literals are defined in section 3.10.5 of the The Java? Language Specification.

上面是jdk源码中对intern方法的详细解释。简单来说就是intern用来返回常量池中的某字符串,如果常量池中已经存在该字符串,则直接返回常量池中该对象的引用。否则,在常量池中加入该对象,然后返回引用。

生成class文件

用字节码分析string和String new 以及String intern原理

用字节码分析string和String new 以及String intern原理

用字节码分析string和String new 以及String intern原理

Classfile /D:/TestApp.class
  Last modified 2019-3-5; size 869 bytes
  MD5 checksum 9093744ea00ada929804a84661bb3119
  Compiled from "TestApp.java"
public class TestApp
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #12.#25        // java/lang/Object."<init>":()V
   #2 = String             #26            // abc
   #3 = Class              #27            // java/lang/String
   #4 = Methodref          #3.#28         // java/lang/String."<init>":(Ljava/lang/String;)V
   #5 = Fieldref           #29.#30        // java/lang/System.out:Ljava/io/PrintStream;
   #6 = Methodref          #3.#31         // java/lang/String.intern:()Ljava/lang/String;
   #7 = Methodref          #32.#33        // java/io/PrintStream.println:(Z)V
   #8 = String             #34            // abcd
   #9 = String             #35            // abcde
  #10 = Methodref          #32.#36        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #11 = Class              #37            // TestApp
  #12 = Class              #38            // java/lang/Object
  #13 = Utf8               <init>
  #14 = Utf8               ()V
  #15 = Utf8               Code
  #16 = Utf8               LineNumberTable
  #17 = Utf8               main
  #18 = Utf8               ([Ljava/lang/String;)V
  #19 = Utf8               StackMapTable
  #20 = Class              #39            // "[Ljava/lang/String;"
  #21 = Class              #27            // java/lang/String
  #22 = Class              #40            // java/io/PrintStream
  #23 = Utf8               SourceFile
  #24 = Utf8               TestApp.java
  #25 = NameAndType        #13:#14        // "<init>":()V
  #26 = Utf8               abc
  #27 = Utf8               java/lang/String
  #28 = NameAndType        #13:#41        // "<init>":(Ljava/lang/String;)V
  #29 = Class              #42            // java/lang/System
  #30 = NameAndType        #43:#44        // out:Ljava/io/PrintStream;
  #31 = NameAndType        #45:#46        // intern:()Ljava/lang/String;
  #32 = Class              #40            // java/io/PrintStream
  #33 = NameAndType        #47:#48        // println:(Z)V
  #34 = Utf8               abcd
  #35 = Utf8               abcde
  #36 = NameAndType        #47:#41        // println:(Ljava/lang/String;)V
  #37 = Utf8               TestApp
  #38 = Utf8               java/lang/Object
  #39 = Utf8               [Ljava/lang/String;
  #40 = Utf8               java/io/PrintStream
  #41 = Utf8               (Ljava/lang/String;)V
  #42 = Utf8               java/lang/System
  #43 = Utf8               out
  #44 = Utf8               Ljava/io/PrintStream;
  #45 = Utf8               intern
  #46 = Utf8               ()Ljava/lang/String;
  #47 = Utf8               println
  #48 = Utf8               (Z)V
{
  public TestApp();
    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 1: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=6, args_size=1
         0: ldc           #2                  // String abc              ///加载常量池中的第2项("abc")到栈中
         2: astore_1                                                     ///将0:中的引用赋值给第1个局部变量,即s1 = "abc"
         3: new           #3                  // class java/lang/String  ///生成String实例
         6: dup                                                          ///复制3:生成对象也就是s2的引用并压入栈中
         7: ldc           #2                  // String abc              ///加载常量池中的第2项("abc")到栈中
         9: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V  ///调用常量池中的第4项,即java/lang/String."<init>"方法。
        12: astore_2                                                     ///将9:中的引用赋值给第2个局部变量,即s2 = new String("abc");
        13: new           #3                  // class java/lang/String  ///生成String实例
        16: dup                                                          ///复制13:生成对象的引用并压入栈中
        17: ldc           #2                  // String abc              ///加载常量池中的第2项("abc")到栈中
        19: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V  ///调用常量池中的第4项,即java/lang/String."<init>"方法。
        22: astore_3                                                     ///将19:中的引用赋值给第3个局部变量,即s3 = new String("abc");
        23: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;  ///获取指定类的静态域,并将其值压入栈顶
        26: aload_2                                                      ///把第2个本地变量也就是s2送到栈顶
        27: aload_1                                                      ///把第1个本地变量也就是s1送到栈顶
        28: invokevirtual #6                  // Method java/lang/String.intern:()Ljava/lang/String;   ///对s1调用String.intern方法返回的是常连池对象的引用#2
        31: if_acmpne     38                                             ///比较栈顶两引用型数值,当结果不相等时跳转  比较s2 == s1.intern()   6:和#2显然不等
        34: iconst_1                                                     ///int型常量值1进栈 也就是true
        35: goto          39                                             ///跳转到39:
        38: iconst_0                                                     ///int型常量值0进栈 也就是false
        39: invokevirtual #7                  // Method java/io/PrintStream.println:(Z)V
        42: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
        45: aload_2                                                      ///把第2个本地变量也就是s2送到栈顶
        46: aload_3                                                      ///把第3个本地变量也就是s3送到栈顶
        47: invokevirtual #6                  // Method java/lang/String.intern:()Ljava/lang/String; ///s3调用String.intern方法返回的是常连池对象的引用#2
        50: if_acmpne     57                                            ///也就是比较s2 == s3.intern()  6:和#2显然不等
        53: iconst_1
        54: goto          58
        57: iconst_0
        58: invokevirtual #7                  // Method java/io/PrintStream.println:(Z)V
        61: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
        64: aload_1                                                      ///把第1个本地变量也就是s1送到栈顶
        65: aload_3                                                      ///把第3个本地变量也就是s3送到栈顶
        66: invokevirtual #6                  // Method java/lang/String.intern:()Ljava/lang/String; //对s3调用String.intern方法返回的是常连池对象的引用#2
        69: if_acmpne     76                                             ///也就是比较s1 == s3.intern()  #2和#2显然相等
        72: iconst_1
        73: goto          77
        76: iconst_0
        77: invokevirtual #7                  // Method java/io/PrintStream.println:(Z)V
        80: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
        83: aload_3
        84: aload_3
        85: invokevirtual #6                  // Method java/lang/String.intern:()Ljava/lang/String;
        88: if_acmpne     95                                             ///也就是比较s3 == s3.intern()  13:和#2显然不等
        91: iconst_1
        92: goto          96
        95: iconst_0
        96: invokevirtual #7                  // Method java/io/PrintStream.println:(Z)V
        99: ldc           #8                  // String abcd
       101: astore        4
       103: new           #3                  // class java/lang/String
       106: dup
       107: ldc           #9                  // String abcde
       109: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
       112: astore        5
       114: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
       117: aload         4
       119: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       122: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
       125: aload         5
       127: invokevirtual #6                  // Method java/lang/String.intern:()Ljava/lang/String;
       130: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       133: return
      LineNumberTable:
        line 5: 0
        line 6: 3
        line 7: 13
        line 9: 23
        line 10: 42
        line 11: 61
        line 12: 80
        line 14: 99
        line 15: 103
        line 16: 114
        line 17: 122
        line 18: 133
      StackMapTable: number_of_entries = 8
        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 ]
        frame_type = 81 /* 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 ]
          stack = [ class java/io/PrintStream, int ]
        frame_type = 81 /* 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 ]
          stack = [ class java/io/PrintStream, int ]
        frame_type = 81 /* 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 ]
          stack = [ class java/io/PrintStream, int ]
}
SourceFile: "TestApp.java"

///是我加的注释。可以参考:《通过反编译深入理解Java String及intern

JVM指令可以自行搜索。

 

关键就是这句:s1 == s3.intern()

s1就是常量池的地址 也就是#2

而s3.intern()直接去找#2

#2==#2

所以是true!

比较s2 == s1.intern()   6:和#2显然不等   6:就是栈上的地址 自然和 常量池地址#2不等啊

总结这样就行: 1.8下  

1. new字符串是会进常量池      

2.相等的字符串常量池自然相等  

3.常量池和栈地址自然不等   
 

上一篇:Java复习总结——String


下一篇:JDK源码分析(1)之 String 相关