《编写高质量代码 改善java程序员的151个建议》
String类是不可改变的量,比如创建了一个“abc”这样的字符串对象,那么它在内存中永远都会是“abc”这样具有固定表面值的一个对象,不能被修改。即使想通过string提供的方法来尝试修改,要么创建一个新的字符串对象,要么返回自己。比如:public String substring(int beginIndex)方法。
public String substring(int beginIndex) { if (beginIndex < 0) { throw new StringIndexOutOfBoundsException(beginIndex); } int subLen = value.length - beginIndex; if (subLen < 0) { throw new StringIndexOutOfBoundsException(subLen); } return (beginIndex == 0) ? this : new String(value, beginIndex, subLen); }
String str1 = "中国"; String str2 = "中国"; String str3 = new String("中国"); String str4 = str3.intern(); System.out.println(str1 == str2); System.out.println(str1 == str3); System.out.println(str1 == str4);输出结果为 true false true
创建一个字符串时,首先检查池中是否有字面值相等的字符串,如果有,则不再创建,直接返回池中该对象的引用,若没有则创建值,然后放到池中,并返回新建对象的引用。通过new String("中国");这种方式 是不会检查字符串池的,也不会把对象放到池中。
通过intern方法,会检查当前的对象在对象池中是否有字面值相同的引用对象,如果有则返回池中对象,如果没有则放置到对象池中,并返回当前对象。
StringBuffer是一个可变字符序列,它与String一样,在内存中保存的都是一个有雪的字符序列(char类型的数组)。
StringBuilder和StringBuffer基本相同,都是可变字符序列,不同点是:StringBuffer 是线程安全的,StringBuilder是线程不安全的,StringBuffer的方法前都有synchronized关键字,所以性能远低于StringBuilder。
三者使用的场景如下:
(1)使用String的场景
在字符串不经常变化的场景中可以使用String类,例如常量的声明、少量的变量运算等。
(2)使用StringBuffer类的场景
在频繁进行字符串的运算(如拼接、替换、删除等),并且运行在多线程的环境中,例如xml解析、http参数解析和封装等。
(3)使用StringBuilder类的场景
在频繁进行字符串的运算(如拼接、替换、删除等),并且运行在单线程的环境中,如SQL语句的拼装、josn的封装等。
字符串拼接方式:+,concat,append
System.out.println("start=" + System.currentTimeMillis()); StringBuffer sb = new StringBuffer("a"); for (int i = 0; i < 50000; i++) { sb.append("c"); } System.out.println("enddd=" + System.currentTimeMillis()); System.out.println("---------------------------------"); System.out.println("start=" + System.currentTimeMillis()); String str = "a"; for (int i = 0; i < 50000; i++) { str += "c"; } System.out.println("enddd=" + System.currentTimeMillis()); System.out.println("---------------------------------"); System.out.println("start=" + System.currentTimeMillis()); String string = "a"; for (int i = 0; i < 50000; i++) { string.concat("c"); } System.out.println("enddd=" + System.currentTimeMillis());
(1)“+”方法拼接字符串
实现方式:
str = new StringBuilder(str).append("c").toString();
它与纯粹使用StringBuilder的append方法是不同的:一是每次循环都会创建一个StringBuilder对象,二是每次执行完毕都要调用toString方法将其转换为字符串---它的执行时间就是耗费在这里了。
(2)concat方法拼接
public String concat(String str) { int otherLen = str.length(); if (otherLen == 0) { return this; } int len = value.length; char buf[] = Arrays.copyOf(value, len + otherLen); str.getChars(buf, len); return new String(buf, true); }
(3)append
public AbstractStringBuilder append(String str) { if (str == null) str = "null"; int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0, len, value, count); count += len; return this; }只有字符串的拼接,没有产生新的对象。
适当的场景使用适当的字符串拼接方式。