Java字符串

本文包含以下内容:

  • String不可变
  • 字符串连接
  • StringBuilder
  • StringBuffer
  • 使用正则表达式
  • 可能出现的坑

String不可变

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

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

StringBuffer可以理解为线程安全版本的StringBuilder,线程安全的StringBuffer相比StringBuilder有些额外的性能开销。 同一个StringBuffer对象可以放在不同的线程中使用,而StringBuilder则不能。

使用正则表达式

正则表达式用来描述一组具有相同特征的字符串,使用正则表达式可以很方便的进行查找和替换操作。String的下列方法同时支持普通字符串和正则表达式:

  • matches
  • replaceAll
  • replaceFirst
  • split

matches方法用来判断整个字符串是否和正则表达式匹配。replaceAll用来替换所有正则表达式匹配到的子字符串,replaceFirst则只替换匹配到第一个子字符串。split用来把字符串从正则表达匹配到的位置拆分开,返回拆分后的字符串数组。关于正则表达式的进阶用法,将在后面的文章中讲解。

可能出现的坑

每个Java对象都有一个toString方法,该方法用来返回一个对象的描述信息,toString方法默认返回对象的地址信息,可以通过覆写来返回定制的内容。如果在toString方法里面使用this连接字符串会出现堆栈溢出错误,示例如下:

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
*/

上面的代码中,toString方法里面的语句把this对象和字符串进行连接操作,编译器会把this转换为字符串,怎么转换呢?答案是调用this.toString()方法,那么问题就出来了,toString方法里面调用toString方法,程序陷入了无限递归调用直到堆栈溢出。在toString方法里面使用this可能是为了获取对象的地址信息,可以通过super.toString()方法来获取地址信息,示例如下:

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。

 

【水煮Java】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值