Java String总结
String 类
String类定代码如下:
/**
* The <code>String</code> class represents character strings. All
* string literals in Java programs, such as <code>"abc"</code>, are
* implemented as instances of this class.
* <p>
* Strings are constant; their values cannot be changed after they
* are created. String buffers support mutable strings.
* Because String objects are immutable they can be shared.
*/
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
}
从代码看有两点需要注意:
- String类为 final 不可以被继承
- String对象一旦创建就不可改变
常量池
常量池常量池指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。包含代码中所定义的各种基本类型(如int、long等等)和对象型(如String及数组)的常量值(final),以及还包含一些以文本形式出现的符号引用,比如:
- 类和接口的全限定名
- 字段的名称和描述符
- 方法和名称和描述符
以字面值出现的字符串会保存在常量池中,以new创建的字符串则会保存在堆中,例如:
String str = "ABC";
这段代码执行时,首现创建一个String类型的引用str,然后再常量池中,查找"ABC",如果找到则str指向"ABC",如果没有找到先将"ABC"放入常量池,再将str指向"ABC"。下面是一个例子:
public class ContactPollTest {
String mString = "ABC";
/**
* @param args
*/
public static void main(String[] args) {
ContactPollTest newTest = new ContactPollTest();
newTest.testConstantPoll();
}
public void testConstantPoll() {
String stra = "ABC";
String strb = "ABC";
String strc = "A" + "BC";
String strd = "A";
String stre = strd + "BC";
final String strf = "A";
String strg = strf + "BC";
String strh = new String("ABC");
System.out.print("testFieldAndVariant stra = mString?:" + (stra == mString) + "\r\n");
System.out.print("testConstantPoll stra = strb?:" + (stra == strb) + "\r\n");
System.out.print("testConstantPoll stra = strc?:" + (stra == strc) + "\r\n");
System.out.print("testConstantPoll stra = stre?:" + (stra == stre) + "\r\n");
System.out.print("testConstantPoll stra = strg?:" + (stra == strg) + "\r\n");
System.out.print("testConstantPoll stra = strh?:" + (stra == strh) + "\r\n");
System.out.print("testConstantPoll stra = stri?:" + (stra == stri) + "\r\n");
System.out.print("testConstantPoll stra = strj?:" + (stra == strj) + "\r\n");
}
static String getString() {
return "BC";
}
final static String sFianlString = "BC";
static String getFinalString() {
return sFianlString;
}
}
输出结果如下:
testFieldAndVariant stra = mString?:true
testConstantPoll stra = strb?:true
testConstantPoll stra = strc?:true
testConstantPoll stra = stre?:false
testConstantPoll stra = strg?:true
testConstantPoll stra = strh?:false
testConstantPoll stra = stri?:false#
解释如下:
- mString 和 stra,strb都指向了常量池中的 "ABC" 因此前行输出为true
- String strc = "A" + "BC" 在编译时被优化为了 String stra = "ABC"因此第三行输出为true
- String stre = strd + "BC",引用了strd,需要重新在堆中创建
- strf为final,因此strg = strf + "BC"; 也被编译器优化为strg = "ABC";
- strh是通过new创建,所以内存分配在堆上,因此最后一行结果为false
- 通过函数获得字符串,都会在堆上创建新的对象
StringBuilder & StringBuffer
String具有不可变性,StringBuilder和StringBuffer用于创建一个可变的字符串,两者的区别在后者是前者的同步版本。下面我们来比较几段代码的性能:
public class StringBuilderTest {
/**
* @param args
*/
public static void main(String[] args) {
testStringConstant();
testStringVariant();
testStringBuilder();
testStringBuffer();
}
static void testStringConstant() {
String a = "";
long begin = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
a += "a";
}
long end = System.currentTimeMillis();
System.out.print("testStringConstant consume:" + (end - begin) + " \r\n");
}
static void testStringVariant() {
String b = "a";
String a = "";
long begin = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
a += b;
}
long end = System.currentTimeMillis();
System.out.print("testStringVariant consume:" + (end - begin) + " \r\n");
}
static void testStringBuilder() {
StringBuilder sb = new StringBuilder();
long begin = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
sb.append("a");
}
long end = System.currentTimeMillis();
System.out.print("testStringBuilder consume:" + (end - begin) + " \r\n");
}
static void testStringBuffer() {
StringBuffer sb = new StringBuffer();
long begin = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
sb.append("a");
}
long end = System.currentTimeMillis();
System.out.print("testStringBuffer consume:" + (end - begin) + " \r\n");
}
}
输出结果为:
testStringConstant consume:3902
testStringVariant consume:3538
testStringBuilder consume:6
testStringBuffer consume:6
性能的差异可以从编译出的字节码看出:
- testStringConstant
[站外图片上传中...(image-e51349-1511918381002)]
从字节码的9行可以看出编译器已经使用StringBuilder进行优化,但是每次循环都会new一个StringBuilder对象。
-
testStringVariant
从字节码的10行可以看出编译器已经使用StringBuilder进行优化,但是每次循环都会new一个StringBuilder对象.
和testStringConstant的性能差距应该在于前者"a"需要在常量池中查找,后者直接通过栈中的引用取值。 -
testStringBuilder & testStringBuffer
从字节码可以看出testStringBuilder函数的写法,可以避免多次创建StringBuilder对象,以及该toString方法。testStringBuffer的情况类似。