- 推荐使用String直接量赋值
推荐直接声明方式:
String str = "a"
String str1 = "china";
String str2 = "china";
String str3 = new String("china");
String str4 = str3.intern();
System.out.println(str1 == str2);
System.out.println(str1 == str3);
System.out.println(str1 == str4);
上面程序的输出结果是:
true
false
true
Java为了避免在一个系统中大量产生String对象,于是设计了一个字符串池(也叫字符串常量池),在字符串池中所容纳的都是String字符串对象,它的创建机制是这样的:创建一个字符串时,检查池中是否有字面值相等的字符串,如果有,则不再创建,直接返回池中该对象的引用,若没有则创建之,然后放回池中,并返回新建对象的引用。
使用new创建字符串时,直接声明一个String对象是不检查字符串池的,也不会把对象放到池中。
intern()会检查当前的对象在对象池中是否有字面值相同的引用对象,如果有则返回池中对象,没有则放置到对象池中,并返回当前对象。
对象放到池中会不会产生线程安全问题?
String是一个不可变对象,这其中包含了两层意思:
-
String类是final类,不可继承,不可能产生一个String的子类
-
在String类提供的所有方法中,如果String有返回值,就会新建一个String对象,不对原对象进行修改,保证了原对象是不可该改变的。
-
注意方法中传递的参数要求
String source = "好是好";
System.out.println(source.replaceAll(source,"是"));
String source1 = "$是$";
System.out.println(source.replaceAll(source1,"是"));
运行结果:
是
$是$
第一个替换成功了,第二个失败了。
/* Note that backslashes ({@code \}) and dollar signs ({@code $}) in the replacement string may cause the results to be different than if it were being treated as a literal replacement string; */
public String replaceAll(String regex, String replacement) {
return Pattern.compile(regex).matcher(this).replaceAll(replacement);
}
可以使用replace方法替代,它是replaceAll的简化版。replaceAll传递的第一个参数是正则表达式。
- 正确使用String、StringBuffer、StringBuilder
String类是不可改变的量,即创建后就不能再修改了。
StringBuffer和StringBuilder都是可变字符序列,不同点是:StringBuffer是线程安全的,StringBuilder是线程不安全的。
在性能方面,由于String类的操作都是产生新的String对象,而StringBuilder和StringBuffer只是一个字符数组的再扩容而已,所以String类的操作要慢于StringBuilder和StringBuffer。
使用场景:
-
String类:字符串不经常变化的场景,如常量的声明、少量的变量运算等
-
StringBuffer类:频繁进行字符串的运算(如拼接、替换、删除等),并且运行在多线程的环境中
-
StringBuilder:频繁进行字符串的运算(如拼接、替换、删除等),并且运行在单线程的环境中
-
注意字符串的位置
System.out.println("apples"+1+2);
System.out.println(1+2+"apples");
输出为:
apples12
3apples
这源于Java对加号的处理机制:在使用加号进行计算的表达式中,只要遇到String字符串,则所有的数据都会转换为String类型进行拼接(如果是基本类型,直接拼接;如果是对象,调用toString方法的返回值然后进行拼接)。
- 自由选择字符串拼接方法
对一个字符串进行拼接有三种方法:
- 加号 ➕
- concat方法
- StringBuilder/StringBuffer的append方法
"+"拼接字符串,类似于如下代码:
str = new StringBuilder(str).append("c").toString();
它与纯粹使用StringBuilder的append方法不同:每次循环都会创建一个StringBuilder对象;每次执行完毕都要调用toString方法;因此执行时间最慢。
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);
}
其整体看上去就是一个数组拷贝,虽然在内存中的处理都是原子性操作,速度非常快,不过最后的return语句,每次concat操作都会新创建一个String对象,这是其速度慢下来的真正原因。
append方法拼接字符串
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
整个append方法都在做字符数组处理,加长,然后数组拷贝,这些都是基本的数据处理,没有新建任何对象,所以速度也就最快。
-
推荐在复杂字符串操作中使用正则表达式
-
强烈建议使用UTF编码
-
对字符串排序持一种宽容心态
《编写高质量代码:改善Java程序的151个建议》