Java技术细节【String intern 深入理解分析】
其实String intern在平时用的真的很少,也用不好,一般都是建议少用,最好不用。
首先这个会让人误解的地方是因为在不同的JDK版本Intern的细节有所不一样导致分歧。
需要明白字符串是放在字符串常量池中,还要知道不同版本的JDK对字符串常量池的改变是怎么样的。
Intern在JDK1.6-和JDK1.7+的区别
-
JDK1.6-的Intern
intern()方法会把将第一次出现的字符串实例复制到永久代(方法区)的字符串常量池中存储,返回的也是永久代里面这个字符串实例的引用(复制过去引用改变不一样)。 -
JDK1.7+的Intern
在JDK1.7的时候,JVM将字符串常量池从方法去中迁移到了堆中,所以intern()方法不需要拷贝字符串到字符串常量池中,因为字符串常量池已经在堆中,只需要在字符串常量池中记录一下第一次出现这个字符串实例的引用就OK。
了解字符串创建的细节
-
字符串创建
String str = new String("ab");
这是一个经常被提问的问题,到底创建了几个对象呢,分别是啥的呢,什么地方呢
以下以JDK1.7+版本:
一般情况下是两个对象,
-
一个是String对象,在堆里。
-
一个是字符串"ab"对象,在开始编译时期就已经放在字符串常量池中,若是运行中会放在堆中,若是调用intern会将字符串引用放在字符串常量池中。
-
-
简单字符串相加
String str = "a" + "b";
这个的主要原理是啥!
L0 LINENUMBER 11 L0 LDC "ab" ASTORE 1 L1 LINENUMBER 12 L1 RETURN L2 LOCALVARIABLE args [Ljava/lang/String; L0 L2 0 LOCALVARIABLE str Ljava/lang/String; L1 L2 1
从字节码中发现,在代码编译的时候(编译器优化),已经将a、b两个字符串组合在一起了,常量池中只会存在ab字符串
-
复杂字符串相加
String str1 = "a"; String str2 = "b"; String str3 = str1 + str2;
这个又是啥意思呢
L0 LINENUMBER 11 L0 LDC "a" ASTORE 1 L1 LINENUMBER 12 L1 LDC "b" ASTORE 2 L2 LINENUMBER 13 L2 NEW java/lang/StringBuilder DUP INVOKESPECIAL java/lang/StringBuilder.<init> ()V ALOAD 1 INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; ALOAD 2 INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String; ASTORE 3 L3 LINENUMBER 14 L3 RETURN L4 LOCALVARIABLE args [Ljava/lang/String; L0 L4 0 LOCALVARIABLE str1 Ljava/lang/String; L1 L4 1 LOCALVARIABLE str2 Ljava/lang/String; L2 L4 2 LOCALVARIABLE str3 Ljava/lang/String; L3 L4 3
这个字节码可以发现,str3的字符串是根据StringBuilder.append一步步添加来的,我们再看看StringBuilder.toString()方法又是啥样的呢
@Override public synchronized String toString() { if (toStringCache == null) { toStringCache = Arrays.copyOfRange(value, 0, count); } return new String(toStringCache, true); }
是将字符数组重新new String()出来的
-
字符串对象相加
String str = new String("a") + new String("b");
这个底层又做了什么出来呢!
L0 LINENUMBER 11 L0 NEW java/lang/StringBuilder DUP INVOKESPECIAL java/lang/StringBuilder.<init> ()V NEW java/lang/String DUP LDC "a" INVOKESPECIAL java/lang/String.<init> (Ljava/lang/String;)V INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; NEW java/lang/String DUP LDC "b" INVOKESPECIAL java/lang/String.<init> (Ljava/lang/String;)V INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder; INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String; ASTORE 1 L1 LINENUMBER 12 L1 RETURN L2 LOCALVARIABLE args [Ljava/lang/String; L0 L2 0 LOCALVARIABLE str Ljava/lang/String; L1 L2 1
以上字节码可以看出,new String()之后还是StringBuilder.append去添加,然后再StringBuilder.toString()与上面一致,最终都是一个字符串对象。
字符串Intern的比较
明白字符串创建的的一些原理之后,对intern的使用有很大的帮助。
首先看一下题目:
String s = new String("a"); s.intern(); String s2 = "a"; System.out.println(s == s2); String s3 = new String("a") + new String("b"); s3.intern(); String s4 = "ab"; System.out.println(s3 == s4);
分析在不同的版本下会出现的结果:
- 如果是JDK6- ,那么运行的结果是false,false
- 如果是JDK7+,运行的结果是false,true