关于java String中intern的深入讲解


本文主要研究一下java String的intern

String.intern()

java.base/java/lang/String.java

public final class String

implements java.io.Serializable, Comparable<String>, CharSequence,

Constable, ConstantDesc {

//......

/**

* Returns a canonical representation for the string object.

* <p>

* A pool of strings, initially empty, is maintained privately by the

* class {@code String}.

* <p>

* When the intern method is invoked, if the pool already contains a

* string equal to this {@code String} object as determined by

* the {@link #equals(Object)} method, then the string from the pool is

* returned. Otherwise, this {@code String} object is added to the

* pool and a reference to this {@code String} object is returned.

* <p>

* It follows that for any two strings {@code s} and {@code t},

* {@code s.intern() == t.intern()} is {@code true}

* if and only if {@code s.equals(t)} is {@code true}.

* <p>

* All literal strings and string-valued constant expressions are

* interned. String literals are defined in section 3.10.5 of the

* <cite>The Java&trade; Language Specification</cite>.

*

* @return a string that has the same contents as this string, but is

* guaranteed to be from a pool of unique strings.

* @jls 3.10.5 String Literals

*/

public native String intern();

//......

}

  • 当调用intern方法时,如果常量池已经包含一个equals此String对象的字符串,则返回池中的字符串
  • 当调用intern方法时,如果常量池没有一个equals此String对象的字符串,将此String对象添加到池中,并返回此String对象的引用(即intern方法返回指向heap中的此String对象引用)
  • 所有literal strings及string-valued constant expressions都是interned的

实例

基于jdk12

StringExistInPoolBeforeIntern

public class StringExistInPoolBeforeIntern {

public static void main(String[] args){

String stringObject = new String("tomcat");

//NOTE 在intern之前,string table已经有了tomcat,因而intern返回tomcat,不会指向stringObject

stringObject.intern();

String stringLiteral = "tomcat";

System.out.println(stringObject == stringLiteral); //false

}

}

  • tomcat这个literal string是interned过的,常量池没有tomcat,因而添加到常量池,常量池有个tomcat;另外由于stringObject是new的,所以heap中也有一个tomcat,而此时它指向heap中的tomcat
  • stringObject.intern()返回的是heap中常量池的tomcat;stringLiteral是tomcat这个literal string,由于常量池已经有该值,因而stringLiteral指向的是heap中常量池的tomcat
  • 此时stringObject指向的是heap中的tomcat,而stringLiteral是heap中常量池的tomcat,因而二者不等,返回false

StringNotExistInPoolBeforeIntern

public class StringNotExistInPoolBeforeIntern {

public static void main(String[] args){

String stringObject = new String("tom") + new String("cat");

//NOTE 在intern之前,string table没有tomcat,因而intern指向stringObject

stringObject.intern();

String stringLiteral = "tomcat";

System.out.println(stringObject == stringLiteral); //true

}

}

  • tom及cat这两个literal string是interned过的,常量池没有tom及cat,因而添加到常量池,常量池有tom、cat;另外由于stringObject是new出来的,是tom及cat二者concat,因而heap中有一个tomcat
  • stringObject的intern方法执行的时候,由于常量池中没有tomcat,因而添加到常量池,intern()返回的是指向heap中的tomcat的引用;stringLiteral是tomcat这个literal string,由于stringObject.intern()已经将tomcat添加到常量池了并指向heap中的tomcat的引用,所以stringLiteral返回的是指向heap中的tomcat的引用
  • 由于stringLiteral返回的是指向heap中的tomcat的引用,其实就是stringObject,因而二者相等,返回true

javap

基于jdk12

StringExistInPoolBeforeIntern

javac src/main/java/com/example/javac/StringExistInPoolBeforeIntern.java

javap -v src/main/java/com/example/javac/StringExistInPoolBeforeIntern.class

Last modified 2019年4月6日; size 683 bytes

MD5 checksum 207635ffd7560f1df24b98607e2ca7db

Compiled from "StringExistInPoolBeforeIntern.java"

public class com.example.javac.StringExistInPoolBeforeIntern

minor version: 0

major version: 56

flags: (0x0021) ACC_PUBLIC, ACC_SUPER

this_class: #8 // com/example/javac/StringExistInPoolBeforeIntern

super_class: #9 // java/lang/Object

interfaces: 0, fields: 0, methods: 2, attributes: 1

Constant pool:

#1 = Methodref #9.#21 // java/lang/Object."<init>":()V

#2 = Class #22 // java/lang/String

#3 = String #23 // tomcat

#4 = Methodref #2.#24 // java/lang/String."<init>":(Ljava/lang/String;)V

#5 = Methodref #2.#25 // java/lang/String.intern:()Ljava/lang/String;

#6 = Fieldref #26.#27 // java/lang/System.out:Ljava/io/PrintStream;

#7 = Methodref #18.#28 // java/io/PrintStream.println:(Z)V

#8 = Class #29 // com/example/javac/StringExistInPoolBeforeIntern

#9 = Class #30 // java/lang/Object

#10 = Utf8 <init>

#11 = Utf8 ()V

#12 = Utf8 Code

#13 = Utf8 LineNumberTable

#14 = Utf8 main

#15 = Utf8 ([Ljava/lang/String;)V

#16 = Utf8 StackMapTable

#17 = Class #31 // "[Ljava/lang/String;"

#18 = Class #32 // java/io/PrintStream

#19 = Utf8 SourceFile

#20 = Utf8 StringExistInPoolBeforeIntern.java

#21 = NameAndType #10:#11 // "<init>":()V

#22 = Utf8 java/lang/String

#23 = Utf8 tomcat

#24 = NameAndType #10:#33 // "<init>":(Ljava/lang/String;)V

#25 = NameAndType #34:#35 // intern:()Ljava/lang/String;

#26 = Class #36 // java/lang/System

#27 = NameAndType #37:#38 // out:Ljava/io/PrintStream;

#28 = NameAndType #39:#40 // println:(Z)V

#29 = Utf8 com/example/javac/StringExistInPoolBeforeIntern

#30 = Utf8 java/lang/Object

#31 = Utf8 [Ljava/lang/String;

#32 = Utf8 java/io/PrintStream

#33 = Utf8 (Ljava/lang/String;)V

#34 = Utf8 intern

#35 = Utf8 ()Ljava/lang/String;

#36 = Utf8 java/lang/System

#37 = Utf8 out

#38 = Utf8 Ljava/io/PrintStream;

#39 = Utf8 println

#40 = Utf8 (Z)V

{

public com.example.javac.StringExistInPoolBeforeIntern();

descriptor: ()V

flags: (0x0001) 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 8: 0

public static void main(java.lang.String[]);

descriptor: ([Ljava/lang/String;)V

flags: (0x0009) ACC_PUBLIC, ACC_STATIC

Code:

stack=3, locals=3, args_size=1

0: new #2 // class java/lang/String

3: dup

4: ldc #3 // String tomcat

6: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V

9: astore_1

10: aload_1

11: invokevirtual #5 // Method java/lang/String.intern:()Ljava/lang/String;

14: pop

15: ldc #3 // String tomcat

17: astore_2

18: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;

21: aload_1

22: aload_2

23: if_acmpne 30

26: iconst_1

27: goto 31

30: iconst_0

31: invokevirtual #7 // Method java/io/PrintStream.println:(Z)V

34: return

LineNumberTable:

line 11: 0

line 13: 10

line 14: 15

line 15: 18

line 16: 34

StackMapTable: number_of_entries = 2

frame_type = 255 /* full_frame */

offset_delta = 30

locals = [ class "[Ljava/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 ]

stack = [ class java/io/PrintStream, int ]

}

SourceFile: "StringExistInPoolBeforeIntern.java"

  • 可以看到常量池有个tomcat

StringNotExistInPoolBeforeIntern

javac src/main/java/com/example/javac/StringNotExistInPoolBeforeIntern.java

javap -v src/main/java/com/example/javac/StringNotExistInPoolBeforeIntern.class

Last modified 2019年4月6日; size 1187 bytes

MD5 checksum 6d173f303b61b8f5826e54bb6ed5157c

Compiled from "StringNotExistInPoolBeforeIntern.java"

public class com.example.javac.StringNotExistInPoolBeforeIntern

minor version: 0

major version: 56

flags: (0x0021) ACC_PUBLIC, ACC_SUPER

this_class: #11 // com/example/javac/StringNotExistInPoolBeforeIntern

super_class: #12 // java/lang/Object

interfaces: 0, fields: 0, methods: 2, attributes: 3

Constant pool:

#1 = Methodref #12.#24 // java/lang/Object."<init>":()V

#2 = Class #25 // java/lang/String

#3 = String #26 // tom

#4 = Methodref #2.#27 // java/lang/String."<init>":(Ljava/lang/String;)V

#5 = String #28 // cat

#6 = InvokeDynamic #0:#32 // #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;

#7 = Methodref #2.#33 // java/lang/String.intern:()Ljava/lang/String;

#8 = String #34 // tomcat

#9 = Fieldref #35.#36 // java/lang/System.out:Ljava/io/PrintStream;

#10 = Methodref #21.#37 // java/io/PrintStream.println:(Z)V

#11 = Class #38 // com/example/javac/StringNotExistInPoolBeforeIntern

#12 = Class #39 // 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 #40 // "[Ljava/lang/String;"

#21 = Class #41 // java/io/PrintStream

#22 = Utf8 SourceFile

#23 = Utf8 StringNotExistInPoolBeforeIntern.java

#24 = NameAndType #13:#14 // "<init>":()V

#25 = Utf8 java/lang/String

#26 = Utf8 tom

#27 = NameAndType #13:#42 // "<init>":(Ljava/lang/String;)V

#28 = Utf8 cat

#29 = Utf8 BootstrapMethods

#30 = MethodHandle 6:#43 // REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;

#31 = String #44 // \u0001\u0001

#32 = NameAndType #45:#46 // makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;

#33 = NameAndType #47:#48 // intern:()Ljava/lang/String;

#34 = Utf8 tomcat

#35 = Class #49 // java/lang/System

#36 = NameAndType #50:#51 // out:Ljava/io/PrintStream;

#37 = NameAndType #52:#53 // println:(Z)V

#38 = Utf8 com/example/javac/StringNotExistInPoolBeforeIntern

#39 = Utf8 java/lang/Object

#40 = Utf8 [Ljava/lang/String;

#41 = Utf8 java/io/PrintStream

#42 = Utf8 (Ljava/lang/String;)V

#43 = Methodref #54.#55 // java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;

#44 = Utf8 \u0001\u0001

#45 = Utf8 makeConcatWithConstants

#46 = Utf8 (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;

#47 = Utf8 intern

#48 = Utf8 ()Ljava/lang/String;

#49 = Utf8 java/lang/System

#50 = Utf8 out

#51 = Utf8 Ljava/io/PrintStream;

#52 = Utf8 println

#53 = Utf8 (Z)V

#54 = Class #56 // java/lang/invoke/StringConcatFactory

#55 = NameAndType #45:#60 // makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;

#56 = Utf8 java/lang/invoke/StringConcatFactory

#57 = Class #62 // java/lang/invoke/MethodHandles$Lookup

#58 = Utf8 Lookup

#59 = Utf8 InnerClasses

#60 = Utf8 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;

#61 = Class #63 // java/lang/invoke/MethodHandles

#62 = Utf8 java/lang/invoke/MethodHandles$Lookup

#63 = Utf8 java/lang/invoke/MethodHandles

{

public com.example.javac.StringNotExistInPoolBeforeIntern();

descriptor: ()V

flags: (0x0001) 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 8: 0

public static void main(java.lang.String[]);

descriptor: ([Ljava/lang/String;)V

flags: (0x0009) ACC_PUBLIC, ACC_STATIC

Code:

stack=4, locals=3, args_size=1

0: new #2 // class java/lang/String

3: dup

4: ldc #3 // String tom

6: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V

9: new #2 // class java/lang/String

12: dup

13: ldc #5 // String cat

15: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V

18: invokedynamic #6, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;

23: astore_1

24: aload_1

25: invokevirtual #7 // Method java/lang/String.intern:()Ljava/lang/String;

28: pop

29: ldc #8 // String tomcat

31: astore_2

32: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;

35: aload_1

36: aload_2

37: if_acmpne 44

40: iconst_1

41: goto 45

44: iconst_0

45: invokevirtual #10 // Method java/io/PrintStream.println:(Z)V

48: return

LineNumberTable:

line 11: 0

line 13: 24

line 14: 29

line 15: 32

line 16: 48

StackMapTable: number_of_entries = 2

frame_type = 255 /* full_frame */

offset_delta = 44

locals = [ class "[Ljava/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 ]

stack = [ class java/io/PrintStream, int ]

}

SourceFile: "StringNotExistInPoolBeforeIntern.java"

InnerClasses:

public static final #58= #57 of #61; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles

BootstrapMethods:

0: #30 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;

Method arguments:

#31 \u0001\u0001

可以看到常量池有tom、cat、tomcat

小结

当调用intern方法时,如果常量池已经包含一个equals此String对象的字符串,则返回池中的字符串

当调用intern方法时,如果常量池没有一个equals此String对象的字符串,将此String对象添加到池中,并返回此String对象的引用(即intern方法返回指向heap中的此String对象引用)

所有literal strings及string-valued constant expressions都是interned的

总结

以上是 关于java String中intern的深入讲解 的全部内容, 来源链接: utcz.com/z/323854.html

回到顶部