【Java基础】String、StringBuilder和StringBuffer

String

String的特性

String是不可变以及线程安全的

为什么String不可变

先来看一下String的源码
注意:此处源码为Java9以及之后的源码

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence,
               Constable, ConstantDesc {
    //@Stable注解表示从null和0变为非null和非0的值,只能有一次;也就是变量最多被修改一次,称为“稳定的”。
    @Stable
    private final byte[] value;
    private final byte coder;

    /** Cache the hash code for the string */
    private int hash; // Default to 0
         

可以发现,String类是由final关键字修饰的类,并且通过final关键字修饰的byte数组来保存字符串
由final修饰的类不能继承,修饰的引用类型变量不能再指向其他对象。
不能被继承则保证子类不能破坏String的不可变性保存字符串的byte数组被final修饰且为私有,通过查看String类源码并未找到能直接修改这个数组的方法
需要注意:不论是String提供的replaceAll()等方法还是字符串拼接用的“+”,本质上还是利用StringBuilder类重新创建一个String对象

public String replaceAll(String replacement) {
        reset();
        boolean result = find();
        if (result) {
            StringBuilder sb = new StringBuilder();
            do {
                appendReplacement(sb, replacement);
                result = find();
            } while (result);
            appendTail(sb);
            return sb.toString();
        }
        return text.toString();
    }

所以String不可变

String不可变有什么好处

线程安全性:由于String是不可变的,可以理解为常量,所以线程安全
支持hash映射和缓存:String的hash值经常被用作Map的键值,因为不可变的特性,所以hash的值也不会改变,不需重新计算

为什么要将String的实现从char[]修改为byte[]

以下是String在JDK8版本的实现

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0

从char[]修改为byte[]主要是为了节约内存,对于新版的String,支持俩种编码方案,一种是Latin-1,另一种是UTF-16(两个字节),如果字符串中包含的字符没有超过 Latin-1 可表示范围内的字符,那就会使用 Latin-1 作为编码方案,超过则用UTF-16。

String的常用方法

length():返回字符串长度。
equals() :此字符串与指定的对象比较。
substring():截取字符串。 equals():字符串比较。
split():分割字符串,返回一个分割后的字符串数组。
indexOf():返回指定字符的索引。
charAt():返回指定索引处的字符。
replace():字符串替换。
trim():去除字符串两端空白。
getBytes():返回字符串的 byte 类型数组。
toLowerCase():将字符串转成小写字母。
toUpperCase():将字符串转成大写字符。

更具体的,请查看https://www.runoob.com/java/java-string.html

String的equals()方法

String 中的 equals 方法是重写过的,比较的是 String 字符串的值是否相等。

    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

而对于Object 的 equals 方法,则是比较的对象的内存地址。

字符串常量池

字符串常量池 是 JVM 为了提升性能和减少内存消耗针对字符串(String 类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。
在创建字符串时,JVM会首先检查字符串常量池,如果字符串已经存在字符串常量池,则返回引用;若不存在,则将其放入常量池并放入其引用。
JDK1.7 之前,字符串常量池存放在永久代(方法区实现)。JDK1.7 字符串常量池和静态变量从永久代移动了 Java 堆中。
原因则是在永久代中,GC执行的效率远低于整堆执行的效率。在Java程序中往往需要大量的String字符串,这就导致也有大量的String字符串等待回收,所以将字符串常量池放入堆中更加。

String s1 = new String(“Hello”)创建了几个字符串对象?

如果字符串常量池存在该对象,则只需要在堆中创建一个字符串对象;若是字符串常量池不存在该对象,则先在字符串常量池中创建,然后在堆空间中创建,因此将创建总共 2 个字符串对象。

String、StringBuilder和StringBuffer的区别

可变性
String不可变
StringBulder和StringBuffer可变,例如他们提供的方法append()

public final class StringBuilder
    extends AbstractStringBuilder
    implements Serializable, CharSequence
public final class StringBuffer
    extends AbstractStringBuilder
    implements 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;
    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;
    }

通过上述三段代码可以发现,StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类;在AbstractStringBuilder 类中也是利用数组来存储字符串的数值,但是并未用final修饰

线程安全性
String是线程安全的;StringBuffer使用synchronized将方法声明为同步方法,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。

总结
(1)操作少量的数据用 String;
(2)多线程操作字符串缓冲区下操作大量数据 StringBuffer;
(3)单线程操作字符串缓冲区下操作大量数据 StringBuilder

考虑到以上特性
String由于是每次拼接都要重新生成新的对象,所以速度最慢;StringBuffer由于要考虑线程安全【append方法的使用synchronized关键字】,在拼接大量数据时,又比StringBuilder效率低一些
所以,一般情况下,字符串拼接速度从快到慢为StringBuilder > StringBuffer > String

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值