【JavaEE】详解String、StringBuffer、StringBuilder

String

String是什么不用多说,这里简述其重要特性。

不可变性

public final class String implements ... {
	...
	private final char value[]
	...
}

这种不可变性主要是由于String对象内部是由一个私有字符数组存储字符,该字符数组被定义为final常量并在JVM中设置了单独的常量池,并且没有提供对char value[]修改的任何方法,也就是其内部存储的字符数组一旦创建既无法被外部调用,也无法修改,还是常量。
这种不可变性使得String对象在多线程环境下是安全的,不需要额外的同步操作。

我们创建一个字符串时,实际上是创建了一个String对象,该对象内部存储一个字符数组。

字符串常量池

String对象可以存储在字符串常量池中。字符串常量池是JVM中的一个特定区域,用于存储字符串常量。当创建一个字符串时,JVM会首先检查字符串常量池中是否已经存在相同值的字符串。如果存在,则直接返回常量池中的引用,而不会创建新的对象。这样可以节省内存空间并提高性能。

在JDK 7之前,字符串常量池是位于方法区中的,但是在JDK 8及以后的版本中,字符串常量池被移动到了堆中。这意味着字符串常量池中的字符串变量也会存储在堆中,而不是方法区。

常见问题:

  1. String为什么可以直接赋值?
    答:String类能直接赋值的原因确实与JVM层面的常量池机制密切相关,而这一机制主要在JVM和编译器层面实现,从源码中并不能直接看出这一点。
  2. String = "abc"String = new String("abc")的区别?
    答:前者是从字符串常量池找"abc",后者是在堆中新创建一个String对象,JDK8之后可以使用intern()方法加入到常量池。理解:
	public class StringExample {
    public static void main(String[] args) {
        String s1 = "abc";  // 字符串字面量,存储在字符串常量池
        String s2 = new String("abc");  // 创建新的字符串对象,存储在堆内存
        String s3 = new String("abc");  // 再创建一个新的字符串对象,存储在堆内存

        System.out.println(s1 == s2);  // false, s1和s2是不同的对象
        System.out.println(s2 == s3);  // false, s2和s3是不同的对象
        System.out.println(s1.equals(s2));  // true, s1和s2的内容相同
        System.out.println(s2.equals(s3));  // true, s2和s3的内容相同
    }
}

字符串操作方法

比如可以直接使用+拼接字符串,length()方法返回字符串长度等。String对象提供了灵活方便的方法处理字符串。

由于字符串的不可变性,每次拼接后实际上是创建新的String对象。使用StringBuilder则可以避免每次都创建对象的开销,只需要在拼接完成后使用toString()创建一次即可。
tips:JDK8之后,+操作符进行字符串拼接时,默认为使用StringBuilder拼接。


StringBuilder

一句话概括:用于创建可变字符串对象,可以动态拼接和修改字符串内容。

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{...}

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    /**
     * The value is used for character storage.
     */
    char[] value;

    /**
     * The count is the number of characters used.
     */
    int count;
	...
}	

通过源码我们会发现,StringBuilder也是final修饰的,也是通过字符数组存储的,那么为什么它是可变的?
核心在于,StringBuilder提供了一系列对该字符数组的修改操作,可以对于原字符数组(这里可以理解为原字符串)本身进行修改,而无需另外创建对象。
其中,最关键的是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;
    }

    /**
    Params:
		srcBegin – index of the first character in the string to copy. 
		srcEnd – index after the last character in the string to copy.
		dst – the destination array. 
		dstBegin – the start offset in the destination array.
    */
    public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
        if (srcBegin < 0) {
            throw new StringIndexOutOfBoundsException(srcBegin);
        }
        if (srcEnd > value.length) {
            throw new StringIndexOutOfBoundsException(srcEnd);
        }
        if (srcBegin > srcEnd) {
            throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
        }
        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
    }

从上面给出的关键源码看出,本质上就是利用了一个数组的复制方法System.arraycopy(),将两个数组合并返回一个新的数组。


StringBuffer

StringBuilder的作用一模一样,区别在于给每一个方法都加上了synchronized关键字,保证了线程安全。

 public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{
    private transient char[] toStringCache;
    ...
    @Override
    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }
    ...
}

String、StringBuffer、StringBuilder三者的区别

通过上述讲解,区别主要在于:

  1. String不可变(由final修饰),如果尝试修改会生成一个新的String对象(浪费内存),StringBuffer和StringBuilder是可变的(在原对象上操作)
  2. StringBuffer是线程安全的(方法都是synchronized修饰),StringBuilder是线程不安全的,所以单线程下后者效率更高。

使用场景,一般情况下字符串使用String,如果需要经常改动字符串,优先使用StringBuilder,作为多线程使用共享变量考虑线程安全时使用StringBuffer

  • 14
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值