从代码分析JDK8中String字符串的创建以及intern方法
下面代码的注释中这句话是重点:
所有的字面字符串和字符串值常量表达式都是interned(我觉得这个可以理解为驻留的)。
但也要注意这种情况:
String str = "see" + "you";
"see"和"you"不会加入常量池,编译器会优化,只把结果"seeyou"加入常量池
package string_;
public class String03 {
public static void main(String[] args) {
//用debug调试分析String的创建
//先看看JDK8中 String的intern方法
/**
* Returns a canonical representation for the string object.
* <p>
* A pool of strings, initially empty, is maintained privately by the
* class String.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
*
* @return a string that has the same contents as this string, but is
* guaranteed to be from a pool of unique strings.
*/
//public native String intern();
/*
上述注释的翻译
返回字符串对象的规范表示形式。
字符串池最初是空的,由类String私下维护。
当调用intern方法时,如果池中已经包含一个等于equals(object)方法确定的string对象的字符串,
那么将返回池中的字符串。否则,该String对象将被添加到池中,并返回对此String对象的引用。
因此,对于任意两个字符串s和t,s.intern() == t.intern() 为真当且仅当s.equals(t)为真。
所有的字面字符串和字符串值常量表达式都是interned(我觉得这个可以理解为驻留的)。
字符串文字在Java™ 语言规范的第3.10.5节中定义。
返回:
与此字符串具有相同内容,但保证来自字符串池中的唯一字符串。
*/
//这句话是重点:
//所有的字面字符串和字符串值常量表达式都是interned(我觉得这个可以理解为驻留的)。
{
/**
* Initializes a newly created String object so that it represents
* the same sequence of characters as the argument; in other words, the
* newly created string is a copy of the argument string. Unless an
* explicit copy of original is needed, use of this constructor is
* unnecessary since Strings are immutable.
*
* @param original
* A String
*/
/*
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
*/
/*
上述注释的翻译
初始化新创建的String对象,使其表示与参数相同的字符序列;
换句话说,新创建的字符串是参数字符串的副本。
除非需要原始的显式副本,否则不需要使用此构造函数,因为字符串是不可变的。
*/
//说明了String(String original)这个构造方法只是copy一个副本,返回的并非参数中的字符串Original
/*
根据下面的代码,可知对于String(String original)这个构造方法
先查看字符串常量池中是否存在跟参数中所填的文本内容相同的字符串引用
若无,则创建一个String Original并在常量池加入Original的引用(网上一些说法说的字符串常量池放的是引用,我比较认可)
若有,则不用往常量池里加入,直接将常量池中的字符串引用赋给Original,并让新创建的String的value和Original的value引用一致
*/
System.out.println("------");
String s2 = "wwwas";
String s3 = new String("wwwas");
//二者value指向一致,但不是同一个String
System.out.println(s2 == s3);//false
//先找字符串常量池有无"qwer"的引用,若有就赋给Original
//没有则创建一个并将引用加入常量池,再赋给Original
//new String("qwer")的返回结果是新的String对象空间的引用,并不是常量池中的字符串引用
String s4 = new String("qwer");
String s5 = s4.intern();//在常量池发现已有,返回常量池的引用(并不是s4的引用)
String s6 = "qwer";
//二者value指向一致,但不是同一个String
System.out.println(s4 == s5);//false
System.out.println(s5 == s6);//true
}
//再看看String(char value[], int offset, int count)这个构造方法
{
/**
* Allocates a new String that contains characters from a subarray
* of the character array argument. The offset argument is the
* index of the first character of the subarray and the count
* argument specifies the length of the subarray. The contents of the
* subarray are copied; subsequent modification of the character array does
* not affect the newly created string.
*
* @param value
* Array that is the source of characters
*
* @param offset
* The initial offset
*
* @param count
* The length
*
* @throws IndexOutOfBoundsException
* If the offset and count arguments index
* characters outside the bounds of the value array
*/
/*
public String(char value[], int offset, int count) {
if (offset < 0) {
throw new StringIndexOutOfBoundsException(offset);
}
if (count <= 0) {
if (count < 0) {
throw new StringIndexOutOfBoundsException(count);
}
if (offset <= value.length) {
this.value = "".value;
return;
}
}
// Note: offset or count might be near -1>>>1.
if (offset > value.length - count) {
throw new StringIndexOutOfBoundsException(offset + count);
}
this.value = Arrays.copyOfRange(value, offset, offset+count);
}
*/
/*
上述注释的翻译
分配一个新的字符串,该字符串包含字符数组参数的子数组中的字符。
偏移量参数是子数组第一个字符的索引,计数参数指定子数组的长度。
子数组的内容被复制;字符数组的后续修改不会影响新创建的字符串。
*/
/*
说明了String(char value[], int offset, int count)
这个构造方法是根据参数中的字符数组的字符,建立一个新的子数组,
根据offset和length确定子数组的字符内容然后以这个子数组作为
创建的字符串String的value
*/
//这个构造方法跟上面的String(String original)不同
//这个构造方法不会在常量池中加入字符串
System.out.println("--------");
String s0 = new String(new char[]{'a', 's', 'd', 'f'}, 0, 4);
String s1 = "asdf";
//二者value的指向不一致,虽然文本都一样
System.out.println(s0 == s1);//false
String ss0 = new String(new char[]{'z', 'x', 'c', 'v'}, 0, 4);
String ss1 = ss0.intern();
//二者是同一个String
System.out.println(ss0 == ss1);//true
}
{
System.out.println("------");
String str00 = new StringBuilder().append("purple").toString();
String str01 = new StringBuilder("zxcvasdf").toString();
String str02 = str00.intern();
String str03 = str01.intern();
String str04 = new StringBuilder().append("pi").append("nk").toString();
String str05 = str04.intern();
System.out.println(str00 == str02);//false
System.out.println(str01 == str03);//false
System.out.println(str04 == str05);//true
//分析:
//“purple"和"zxcvasdf"作为字面字符串被interned加入到常量池了
// 在str04.intern()之前没有把完整的"pink"加入到常量池
}
//跟上面的拼接不同,注意下面的情况
{
System.out.println("------");
String s0 = "hello" + "MAN";
//常量池中有无"hello"呢?
String s1 = new StringBuilder("hel").append("lo").toString();
String s2 = s1.intern();
System.out.println(s1 == s2);//true
//说明在s1.intern()之前不存在
//再看看拼接后的结果"GoodBye"在不在常量池
String s3 = "Good" + "Bye";
String s4 = new StringBuilder("Good").append("Bye").toString();
String s5 = s4.intern();
System.out.println(s4 == s5);//false
System.out.println(s5 == s3);//true
//说明拼接后的结果在常量池
//编译器编译时把上面的语句"Good" + "Bye"优化成了"GoodBye"
}
}
}
看看反编译的结果,和上面代码分析的一样