本文包含以下内容:
Java中字符串使用String对象表示,它是不可变的,调用String对象的任何方法都不会改变字符串的内容,但是它会返回一个全新的字符串来表示方法操作的结果。
String original = "hello world!";
String newString = original.toUpperCase();
System.out.println(original);
System.out.println(newString);
/*程序输出
hello world!
HELLO WORLD!
*/
上例中,调用toUpperCase方法没有改变原始字符串的内容而是生成了一个新的字符串。
在实际开发中字符串的连接操作使用很频繁,很多编程语言中字符串连接使用“+”操作符,Java也支持通过“+”操作符连接字符串。
String prefix = "hello";
String postfix = "world!";
String whole = prefix + " " + postfix;
System.out.println(whole);
/*程序输出
hello world!
*/
由于字符串的不可变性,每个连接操作符“+”的调用会产生一个新的对象,如果连接的字符串过多则会产生很多新的中间对象,而我们只使用最终的对象。上例中prefix + " "会产生一个新的中间对象,这个对象不是我们需要的,我们需要的是连接最终生成的whole对象。大量字符串连接会产生一堆需要垃圾回收的中间对象,所以它会影响应用程序的性能。
StringBuilder用来辅助生成String,可以把StringBuilder理解为可变的String,可变意味着调用StringBuilder对象的方法会改变字符串内容,对StringBuilder的操作不会生成中间对象。字符串的连接操作可以使用StringBuilder的append方法来代替:
StringBuilder sb = new StringBuilder();
sb.append("hello");
sb.append(" ");
sb.append("world!");
System.out.println(sb.toString());
/*程序输出
hello world!
*/
相比字符串连接操作,StringBuilder性能要好一些,因为它不会产生中间对象给垃圾回收器制造负担。
鉴于字符串连接的低效性,编译器对其进行了优化,优化方式正是用StringBuilder的append方法代替字符串连接操作符“+”。但是优化毕竟有限的,很多优化还得使用者来做,以下是编译器优化的一个示例:
//编译器优化以前的代码
public String example(String[] fields)
{
String result = "";
for (String str: fields) {
result += str;
}
return result;
}
//编译器优化后的示例代码
//以下代码只是为了方便理解编译器优化的示例代码
public String example(String[] fields)
{
String result = "";
for (String str: fields) {
StringBuilder sb = new StringBuilder(result);
sb.append(str);
result = sb.toString();
}
return result;
}
可以看到在循环里面使用字符连接操作符时,每次循环都会创建一个StringBuilder对象,这显然不是最优解。我们可以直接使用StringBuilder做优化,示例如下:
//主动优化的代码
public String example(String[] fields)
{
StringBuilder sb = new StringBuilder();
for (String str: fields) {
sb.append(str);
}
return sb.toString();
}
在没有循环时编译器的优化是值得信赖的,可以直接使用字符串连接操作符“+”,当涉及到循环时编译器优化可能不是最好的,这时可以考虑使用StringBuilder来优化程序。
StringBuffer可以理解为线程安全版本的StringBuilder,线程安全的StringBuffer相比StringBuilder有些额外的性能开销。 同一个StringBuffer对象可以放在不同的线程中使用,而StringBuilder则不能。
正则表达式用来描述一组具有相同特征的字符串,使用正则表达式可以很方便的进行查找和替换操作。String的下列方法同时支持普通字符串和正则表达式:
matches方法用来判断整个字符串是否和正则表达式匹配。replaceAll用来替换所有正则表达式匹配到的子字符串,replaceFirst则只替换匹配到第一个子字符串。split用来把字符串从正则表达匹配到的位置拆分开,返回拆分后的字符串数组。关于正则表达式的进阶用法,将在后面的文章中讲解。
public class Test {
public String toString() {
return "Test " + this;
}
public static void main(String[] args) {
Test t = new Test();
System.out.println(t.toString());
}
}
/*输出:
抛出java.lang.StackOverflowError
*/
public class Test {
public String toString() {
return "Test " + super.toString();
}
public static void main(String[] args) {
Test t = new Test();
System.out.println(t.toString());
}
}
/*输出:
Test com.shaoshuidashi.Test@154617c
*/
字符串是一种比较常用的工具,在实际使用时需要注意效率问题,当一个字符串是通过一个循环体构造而来时,我们脑海中应该马上想到StringBuilder。