String的创?/font>
String s = "hello";
JVM先根据内?hello"查找对象Q如果没有找刎ͼ则在heap上创建新对象Qƈ其赋予s1Q否则用已l存在的对象
String s = new String("hello");
JVM直接在heap上创建新的对象,所以在heap中会出现内容相同Q地址不同的String对象
String的比?/font>
"==" 比较地址
"equals" 比较内容
举例:
String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");
s1 == s2; // true 地址相同
s1 == s3; // false 地址不同
s1.equals(s2); // true 内容相同
s1.equals(s3); // true 内容相同
intern() Ҏ
查找内容相同(equals())的字W串
String s1 = "hello"; // hello不存在,jvm创徏新对?(1)
String s2 = new String("hello"); // 创D新对?(2)Q这时heap中存在两个内容ؓhello的对?br minmax_bound="true" />
s1 == s2; // false // 地址不同
s1.equals(s2); // true // 内容相同
s2 = s2.intern(); // true // 扑ֈ对象(1) q赋予s2
s1 == s2; // true !! // 注意Q此时s1,s2同指?1)
效率QString ?StringBuffer
情景1Q?br minmax_bound="true" />
(1) String result = "hello" + " world";
(2) StringBuffer result = new String().append("hello").append(" world");
(1) 的效率好?(2)Q不要奇怪,q是因ؓJVM会做如下处理
~译?nbsp; String result = "hello" + " world";
~译?nbsp; String result = "hello world";
情景2Q?br minmax_bound="true" />
(1) public String getString(String s1, String s2) {
return s1 + s2;
}
(2) public String getString(String s1, String s2) {
return new StringBuffer().append(s1).append(s2);
}
(1) 的效率与 (2) 一Pq是因ؓJVM会做如下处理
~译?nbsp; return s1 + s2;
~译?nbsp; return new StringBuffer().append(s1).append(s2);
情景3Q?br minmax_bound="true" />
(1) String s = "s1";
s += "s2";
s += "s3";
(2) StringBuffer s = new StringBuffer().append("s1").append("s2").append("s3");
(2) 的效率好?1)Q因为String是不可变对象Q每?+="操作都会造成构造新的String对象
情景4Q?br minmax_bound="true" />
(1) StringBuffer s = new StringBuffer();
for (int i = 0; i < 50000; i ++) {
s.append("hello");
}
(2) StringBuffer s = new StringBuffer(250000);
for (int i = 0; i < 50000; i ++) {
s.append("hello");
}
(2) 的效率好?(1)Q因为StringBuffer内部实现是char数组Q默认初始化长度?6Q每当字W串长度大于char
数组长度的时候,JVM会构造更大的新数l,q将原先的数l内容复制到新数l,(2)避免了复制数l的开销
关键?br minmax_bound="true" />
1). 单的认ؓ .append() 效率好于 "+" 是错误的Q?br minmax_bound="true" />
2). 不要使用 new 创徏 String
3). 注意 .intern() 的?br minmax_bound="true" />
4). 在编译期能够定字符串值的情况下,使用"+"效率最?br minmax_bound="true" />
5). 避免使用 "+=" 来构造字W串
6). 在声明StringBuffer对象的时候,指定合适的capacityQ不要用默认?18)
7). 注意以下二者的区别不一?br minmax_bound="true" />
- String s = "a" + "b";
- String s = "a";
s += "b";
String和StringBuffer之概?br minmax_bound="true" />
创徏字符串的较佳途径
滞留字符串带来的优化
q接字符串时的优化技?br minmax_bound="true" />
借助StringBuffer的初始化q程的优化技?br minmax_bound="true" />
关键?/font>
String和StringBuffer之概?/strong>
非可变对象一旦创Z后就不能再被改变Q可变对象则可以在创Z后被改变。String对象是非可变对象QStringBuffer对象则是可变对象。ؓ获得更佳的性能你需要根据实际情况小心}慎地选择到底使用q两者中的某一个。下面的话题会作详细的阐q。(注意Q这个章节假设读者已l具备Java的String和StringBuffer的相兛_知识。)
创徏字符串的较佳途径
你可以按照以下方式创建字W串对象Q?br minmax_bound="true" />
1. String s1 = "hello";
String s2 = "hello";
2. String s3 = new String("hello");
String s4 = new String("hello");
上面哪种方式会带来更好的性能呢?下面的代码片断用来测量二者之间的区别?/font>
StringTest1.java
package com.performance.string;
/** This class shows the time taken for creation of
* String literals and String objects.
*/
public class StringTest1 {
public static void main(String[] args){
// create String literals
long startTime = System.currentTimeMillis();
for(int i=0;i<50000;i++){
String s1 = "hello";
String s2 = "hello";
}
long endTime = System.currentTimeMillis();
System.out.println("Time taken for creation of String literals : "
+ (endTime - startTime) + " milli seconds" );
// create String objects using 'new' keyword
long startTime1 = System.currentTimeMillis();
for(int i=0;i<50000;i++){
String s3 = new String("hello");
String s4 = new String("hello");
}
long endTime1 = System.currentTimeMillis();
System.out.println("Time taken for creation of String objects : "
+ (endTime1 - startTime1)+" milli seconds");
}
}
q段代码的输出:
Time taken for creation of String literals : 0 milli seconds
Time taken for creation of String objects : 170 milli seconds
JVM是怎样处理字符串的呢?
Java虚拟Zl护一个内部的滞留字符串对象的列表Q唯一字符串的池)来避免在堆内存中产生重复的String对象。当JVM从class文g里加载字W串字面量ƈ执行的时候,它会先检查一下当前的字符串是否已l存在于滞留字符串列表,如果已经存在Q那׃会再创徏一个新的String对象而是引用指向已l存在的String对象QJVM会在内部为字W串字面量作q种查,但ƈ不会为通过new关键字创建的String对象作这U检查。当然你可以明确C用String.intern()Ҏ强制JVM为通过new关键字创建的String对象作这L查。这样可以强制JVM查内部列表而用已有的String对象?/font>
所以结论是QJVM会内在地为字W串字面量维护一些唯一的String对象Q程序员不需要ؓ字符串字面量而发愁,但是可能会被一些通过new关键字创建的String对象而困扎ͼ不过他们可以使用intern()Ҏ来避免在堆内存上创徏重复的String对象来改善Java的运行性能。下一节会向大家展示更多的信息?/font>
下图展示了未使用intern()Ҏ来创建字W串的情c?br minmax_bound="true" />
你可以自׃?=操作W和String.equals()Ҏ来编码测试上面提到的区别?=操作W会q回true如果一些引用指向一个相同的对象但不会判断String对象的内Ҏ否相同;String.equals()Ҏ会返回true如果被操作的String对象的内容相同。对于上面的代码会有s1==s2Q因为s1和s2两个引用指向同一个对象,对于上面的代码,s3.equals(s4)会返回true因ؓ两个对象的内定w一样ؓ”hello”。你可以从上囄U机制。在q里有三个独立的包含了相同的内容Q?#8221;hello”Q的对象Q实际上我们不需要这么三个独立的对象――因q行它们的话既浪Ҏ间又费内存?/font>
那么怎样才能保String对象不会重复呢?下一个话题会늛对于内徏String机制的兴?/font>
滞留字符串的优化作用
同一个字W串对象被重复地创徏是不必要的,String.intern()Ҏ可以避免q种情况。下图说明了String.intern()Ҏ是如何工作的QString.intern()Ҏ查字W串对象的存在性,如果需要的字符串对象已l存在,那么它会引用指向已l存在的字符串对象而不是重新创Z个。下图描l了使用了intern()Ҏ的字W串字面量和字符串对象的创徏情况?br minmax_bound="true" />

下面的例E帮助大家了解String.intern()Ҏ的重要性?br minmax_bound="true" />
StringTest2.java
package com.performance.string;
// This class shows the use of intern() method to improve performance
public class StringTest2 {
public static void main(String[] args){
// create String references like s1,s2,s3...so on..
String variables[] = new String[50000];
for( int i=0;i variables[i] = "s"+i;
}
// create String literals
long startTime0 = System.currentTimeMillis();
for(int i=0;i variables[i] = "hello";
}
long endTime0 = System.currentTimeMillis();
System.out.println("Time taken for creation of String literals : "
+ (endTime0 - startTime0) + " milli seconds" );
// create String objects using 'new' keyword
long startTime1 = System.currentTimeMillis();
for(int i=0;i variables[i] = new String("hello");
}
long endTime1 = System.currentTimeMillis();
System.out.println("Time taken for creation of String objects with 'new' key word : "
+ (endTime1 - startTime1)+" milli seconds");
// intern String objects with intern() method
long startTime2 = System.currentTimeMillis();
for(int i=0;i variables[i] = new String("hello");
variables[i] = variables[i].intern();
}
long endTime2 = System.currentTimeMillis();
System.out.println("Time taken for creation of String objects with intern(): "
+ (endTime2 - startTime2)+" milli seconds");
}
}
q是上面那段代码的输出结果:
Time taken for creation of String literals : 0 milli seconds
Time taken for creation of String objects with 'new' key word : 160 milli seconds
Time taken for creation of String objects with intern(): 60 milli seconds
q接字符串时候的优化技?br minmax_bound="true" />
你可以?操作W或者String.concat()或者StringBuffer.append(){办法来q接多个字符Ԍ那一U办法具有最佳的性能呢?
如何作出选择取决于两U情景,W一U情景是需要连接的字符串是在编译期军_的还是在q行期决定的Q第二种情景是你使用的是StringBufferq是String。通常E序员会认ؓStringBuffer.append()Ҏ会优?操作W或String.concat()ҎQ但是在一些特定的情况下这个假x不成立的?/font>
1) W一U情景:~译期决定相对于q行期决?br minmax_bound="true" />
L下面的StringTest3.java代码和输出结果?/font>
package com.performance.string;
/** This class shows the time taken by string concatenation at compile time and run time.*/
public class StringTest3 {
public static void main(String[] args){
//Test the String Concatination
long startTime = System.currentTimeMillis();
for(int i=0;i<5000;i++){
String result = "This is"+ "testing the"+ "difference"+ "between"+
"String"+ "and"+ "StringBuffer";
}
long endTime = System.currentTimeMillis();
System.out.println("Time taken for string concatenation using + operator : "
+ (endTime - startTime)+ " milli seconds");
//Test the StringBuffer Concatination
long startTime1 = System.currentTimeMillis();
for(int i=0;i<5000;i++){
StringBuffer result = new StringBuffer();
result.append("This is");
result.append("testing the");
result.append("difference");
result.append("between");
result.append("String");
result.append("and");
result.append("StringBuffer");
}
long endTime1 = System.currentTimeMillis();
System.out.println("Time taken for String concatenation using StringBuffer : "
+ (endTime1 - startTime1)+ " milli seconds");
}
}
q是上面的代码的输出l果Q?br minmax_bound="true" />
Time taken for String concatenation using + operator : 0 milli seconds
Time taken for String concatenation using StringBuffer : 50 milli seconds
很有地Q?操作W居然比StringBuffer.append()Ҏ要快Qؓ什么呢Q?/font>
q里~译器的优化起了关键作用Q编译器像下面D例的那样单地在编译期q接多个字符丌Ӏ它使用~译期决定取代运行期军_Q在你用new关键字来创徏String对象的时候也是如此?/font>
~译前:
String result = "This is"+"testing the"+"difference"+"between"+"String"+"and"+"StringBuffer";
~译后:
String result = "This is testing the difference between String and StringBuffer";
q里String对象在编译期决定了而StringBuffer对象是在q行期决定的。运行期军_需要额外的开销当字W串的值无法预先知道的时候,~译期决定作用于字符串的值可以预先知道的时候,下面是一个例子?/font>
~译前:
public String getString(String str1,String str2) {
return str1+str2;
}
~译后:
return new StringBuffer().append(str1).append(str2).toString();
q行期决定需要更多的旉来运行?/font>
2) W二U情景:使用StringBuffer取代String
看看下面的代码你会发C情景一相反的结果――连接多个字W串的时候StringBuffer要比String快?br minmax_bound="true" />
StringTest4.java
package com.performance.string;
/** This class shows the time taken by string concatenation
using + operator and StringBuffer */
public class StringTest4 {
public static void main(String[] args){
//Test the String Concatenation using + operator
long startTime = System.currentTimeMillis();
String result = "hello";
for(int i=0;i<1500;i++){
result += "hello";
}
long endTime = System.currentTimeMillis();
System.out.println("Time taken for string concatenation using + operator : "
+ (endTime - startTime)+ " milli seconds");
//Test the String Concatenation using StringBuffer
long startTime1 = System.currentTimeMillis();
StringBuffer result1 = new StringBuffer("hello");
for(int i=0;i<1500;i++){
result1.append("hello");
}
long endTime1 = System.currentTimeMillis();
System.out.println("Time taken for string concatenation using StringBuffer : "
+ (endTime1 - startTime1)+ " milli seconds");
}
}
q是上面的代码的输出l果Q?br minmax_bound="true" />
Time taken for string concatenation using + operator : 280 milli seconds
Time taken for String concatenation using StringBuffer : 0 milli seconds
看得出StringBuffer.append()Ҏ要比+操作W要快得多,Z么呢Q?/font>
原因是两者都是在q行期决定字W串对象Q但?操作W用不同于StringBuffer.append()的规则通过String和StringBuffer来完成字W串q接操作。(译注Q什么样的规则呢Q)
借助StringBuffer的初始化q程的优化技?/strong>
你可以通过StringBuffer的构造函数来讑֮它的初始化容量,q样可以明显地提升性能。这里提到的构造函数是StringBuffer(int length)Qlength参数表示当前的StringBuffer能保持的字符数量。你也可以用ensureCapacity(int minimumcapacity)Ҏ在StringBuffer对象创徏之后讄它的定w。首先我们看看StringBuffer的缺省行为,然后再找Z条更好的提升性能的途径?/font>
StringBuffer的缺省行为:
StringBuffer在内部维护一个字W数l,当你使用~省的构造函数来创徏StringBuffer对象的时候,因ؓ没有讄初始化字W长度,StringBuffer的容量被初始化ؓ16个字W,也就是说~省定w是16个字W。当StringBuffer辑ֈ最大容量的时候,它会自w容量增加到当前?倍再?Q也是Q?*旧?2Q?/font>
如果你用缺省|初始化之后接着往里面q加字符Q在你追加到W?6个字W的时候它会将定w增加?4Q?*16+2Q,当追加到34个字W的时候就会将定w增加?0Q?*34+2Q。无Z事只要StringBuffer到达它的最大容量它׃得不创徏一个新的字W数l然后重新将旧字W和新字W都拯一遍――这也太昂贵了点。所以LlStringBuffer讄一个合理的初始化容量值是错不了的Q这样会带来立竿见媄的性能增益?/font>
我利用两个StringBuffer重新试了上面的StringTest4.java代码Q一个未使用初始化容量D另一个用了。这ơ我q加?0000?#8217;hello’对象没有使用+操作W。区别是我用StringBuffer(250000)的构造函数来初始化第二个StringBuffer了?/font>
输出l果如下Q?br minmax_bound="true" />
Time taken for String concatenation using StringBuffer with out setting size: 280 milli seconds
Time taken for String concatenation using StringBuffer with setting size: 0 milli seconds
StringBuffer初始化过E的调整的作用由此可见一斑。所以,使用一个合适的定w值来初始化StringBuffer永远都是一个最佳的?/font>
关键?/strong>
1. 无论何时只要可能的话使用字符串字面量来常见字W串而不是用new关键字来创徏字符丌Ӏ?br minmax_bound="true" />
2. 无论何时当你要用new关键字来创徏很多内容重复的字W串的话Q请使用String.intern()Ҏ?br minmax_bound="true" />
3. +操作W会为字W串q接提供最佳的性能――当字符串是在编译期军_的时候?br minmax_bound="true" />
4. 如果字符串在q行期决定,使用一个合适的初期定w值初始化的StringBuffer会ؓ字符串连接提供最佳的性能?/font>
String与StringBuffer的比?/strong>
StringcL供了一些方法,用来q行字符串的比较。这个类实现了Object父类的equals()ҎQ用来比较两U字W串的值是否相{。同时还增加了equalsIgnoreCase()Ҏ可以忽略两个字符串大写的区别。下面是q两U方法的例子?br minmax_bound="true" />
【例6-6?br minmax_bound="true" />
public class E6_6{
public static void main(String args[]) {
String s1="a";
String s2=new String("a");
String s3="A";
System.out.println(s1.equals(s2));
System.out.println(s1.equals(s3));
System.out.println(s1.equalsIgnoreCase(s3));
}
}
上例的输出是
true
flase
true
但是StringBuffercdƈ没有实现ObjcetcȝEqualsҎQ所以不能用q个Ҏ来比较两个StringBuffercȝ字符串是否相{,如下例所C?br minmax_bound="true" />
【例6-7?br minmax_bound="true" />
public class E6_7{
public static void main(String args[]) {
StringBuffer s1=new StringBuffer("a");
StringBuffer s2=new StringBuffer("a");
System.out.println(s1.equals(s2));
}
}
E序输出Qfalse
除了用equalseҎ来比较两个字W串外,q可以用==来比较字W串。与equalseҎ不同的是Q?=不是比较两个字符串的值是否相{,而是比较几个字符串的引用是否指向同一个实例。如?-8所C?br minmax_bound="true" />
【例6-8?br minmax_bound="true" />
public class E6_8{
public static void main(String args[]) {
String s1="a";
String s2="a";
String s3=new String("a");
String s4=new String("a");
System.out.println(s1==s2);
System.out.println(s3==s4);
System.out.println(s1==s3);
}
}
上面的程序段输出Q?br minmax_bound="true" />
true
false
false
与上例进行比较,不仅可以看出?=与equals的区别,q可以看到字面量的String的特D之外?br minmax_bound="true" />
对于字面量的StringQ只要字W串的值是相等的,不论有多个引用都是指向同一块内存,不再另外分配I间。而用new关键字生成的实例则不同,每当用new实例化一ơ,分配该实例自q内存I间。上例的存储方式如图所C:
?-1 s1、s2、s3、s4的区?br minmax_bound="true" />
下面再通过另一个例子来看String和StringBuffer的区别?br minmax_bound="true" />
【例6-9?br minmax_bound="true" />
public class E6_9{
public static void main(String args[]) {
String s1="a";
StringBuffer sb1=new StringBuffer("a");
StringBuffer sb2=sb1;
String s2="a"+"b";
sb1.append("b");
System.out.println(s1==s2);
System.out.println(sb1==sb2);
}
}
上例输出的是Q?br minmax_bound="true" />
flase
true
上例可以证明q样的结论:String是不可变长的字符Ԍ对String做Q何的修改生成新的字W串Q而StringBuffer是可变长的字W串Q不论怎么更动q是同一个字W串?br minmax_bound="true" />

]]>