我正在与我的同事的下一个谜语挣扎:
public class App1 { public static void main(String[] args) { String s1 = "Ja".concat("va"); // seems to be interned?! String s2 = s1.intern(); System.out.println(s1 == s2); // true } }
这输出是真的.我有点惊讶,因为看起来s1是实习生.但这不是常数表达,不是吗?
但后来我更加惊讶为什么以下打印错误.
public class App2 { public static void main(String[] args) { String s1 = "Ja".concat("va"); // seems not to be interned?! String s3 = new String("Java"); // this changes output String s2 = s1.intern(); System.out.println(s1 == s2); // false } }
为什么引入s3会改变输出?
解决方法
@H_404_15@ 以下是管理Java String对象wrt到String池的规则:>使用String文字创建String对象时,JVM会检查池中是否已存在字符串文字.如果池中存在该对象,则返回相同的对象而不是新对象.
>使用new运算符创建String对象时,即使字符串池中存在该字符串,也会创建新对象.
>在String对象上调用intern方法时,会创建一个新的String对象并将其放在池中(如果该对象不存在). intern方法从池中返回对象.
让我们回顾一下你的例子,
String s1 = "Ja".concat("va");
如果你看一下String source中的concat操作,你会注意到它最后会调用new运算符.
new String(buf,true)
因此,s1不会添加到字符串池中.
现在,让我们看一下实习生所在的行,
String s2 = s1.intern();
这里,s1上的intern方法从String池返回该对象(如果它不存在则创建).因此,s2包含String池中的对象.
同时,s1仍然包含旧对象而不是池中的对象.因此,
(s1 == s2)
永远都会回归虚假.
Java 1.8.0_92-b14中的修改行为
Java 8中的行为已更改. Java编译器正在执行优化.如果在concat之后立即调用intern方法,Java 8将优化并在字符串池中创建字符串对象,并忽略我们在早期Java版本中见过的new的早期行为.请检查反编译代码的操作码中的优化(checkOne是App1,checkTwo是App2),
public static void checkOne(); descriptor: ()V flags: ACC_PUBLIC,ACC_STATIC Code: stack=3,locals=2,args_size=0 0: ldc #2 // String Ja 2: ldc #3 // String va 4: invokevirtual #4 // Method java/lang/String.concat:(Ljava/lang/String;)Ljava/lang/String; 7: astore_0 8: aload_0 9: invokevirtual #5 // Method java/lang/String.intern:()Ljava/lang/String; 12: astore_1 13: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream; 16: aload_0 17: aload_1 18: if_acmpne 25 21: iconst_1 22: goto 26 25: iconst_0 26: invokevirtual #7 // Method java/io/PrintStream.println:(Z)V 29: return LineNumberTable: line 6: 0 line 7: 8 line 9: 13 line 10: 29 LocalVariableTable: Start Length Slot Name Signature 8 22 0 s1 Ljava/lang/String; 13 17 1 s2 Ljava/lang/String; StackMapTable: number_of_entries = 2 frame_type = 255 /* full_frame */ offset_delta = 25 locals = [ class java/lang/String,class java/lang/String ] stack = [ class java/io/PrintStream ] frame_type = 255 /* full_frame */ offset_delta = 0 locals = [ class java/lang/String,class java/lang/String ] stack = [ class java/io/PrintStream,int ] public static void checkTwo(); descriptor: ()V flags: ACC_PUBLIC,locals=3,args_size=0 0: ldc #2 // String Ja 2: ldc #3 // String va 4: invokevirtual #4 // Method java/lang/String.concat:(Ljava/lang/String;)Ljava/lang/String; 7: astore_0 8: new #8 // class java/lang/String 11: dup 12: ldc #9 // String Java 14: invokespecial #10 // Method java/lang/String."":(Ljava/lang/String;)V 17: astore_1 18: aload_0 19: invokevirtual #5 // Method java/lang/String.intern:()Ljava/lang/String; 22: astore_2 23: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream; 26: aload_0 27: aload_2 28: if_acmpne 35 31: iconst_1 32: goto 36 35: iconst_0 36: invokevirtual #7 // Method java/io/PrintStream.println:(Z)V 39: return LineNumberTable: line 13: 0 line 14: 8 line 15: 18 line 17: 23 line 18: 39 LocalVariableTable: Start Length Slot Name Signature 8 32 0 s1 Ljava/lang/String; 18 22 1 s3 Ljava/lang/String; 23 17 2 s2 Ljava/lang/String; StackMapTable: number_of_entries = 2 frame_type = 255 /* full_frame */ offset_delta = 35 locals = [ class java/lang/String,class java/lang/String,int ]