String StringBuffer 和 StringBuilder

1. String StringBuffer 和 StringBuilder 的区别是什么 String 为什么是不可变的

简单的来说: String 类中使用 final 关键字字符数组保存字符串, private
final char value[],所以 String 对象是不可变的。而 StringBuilder 与
StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中
也是使用字符数组保存字符串 char[]value 但是没有用 final 关键字修饰,所以
这两种对象都是可变的。
StringBuilder 与 StringBuffer 的构造方法都是调用父类构造方法也就是
AbstractStringBuilder 实现的,大家可以自行查阅源码。

AbstractStringBuilder.java
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
int count;
AbstractStringBuilder() {
}
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}


线程安全性
String 中的对象是不可变的,也就可以理解为常量,线程安全。
AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义了
一些字符串的基本操作,如 expandCapacity、 append、 insert、 indexOf 等公
共方法。 StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以
是线程安全的。 StringBuilder 并没有对方法进行加同步锁,所以是非线程安全
的。
性能
每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将
指针指向新的 String 对象。 StringBuffer 每次都会对 StringBuffer 对象本身
进行操作,而不是生成新的对象并改变对象引用。相同情况下使用
StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,
但却要冒多线程不安全的风险。
对于三者使用的总结:
1. 操作少量的数据 = String
2. 单线程操作字符串缓冲区下操作大量数据 = StringBuilder
3. 多线程操作字符串缓冲区下操作大量数据 = StringBuffer

String 的equals方法

 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;
    }

从代码段中可以看到,equals方法比较的是字符串是否相等,首先比较指针是否相等,相等直接返回true,最后取出他们的final数组,进行每个字符的比较,记住字符是基本数据类型,可以直接比较。

下面是== 和 equals的区别

package com.example.base.string;

public class MyStringTest {

    public static void main(String args[]) {
        testString();
    }

    private static void testString() {
        String hello = "Hello";
        String hello2 = new String("Hello");
        String lo = "lo";
        //false. ==比较的是引用.hello是一个字符串常量表达式,并实现为调用interned,以共享不同的示例(string pool)
        //hello2是一个对象,指向的是堆上的一个地址
        System.out.println(hello == hello2);            //false
        //true. equals比较的是两个对象的值
        System.out.println(hello.equals(hello2));       //true
        //true. intern()会检查当前字符串池是否包含这个字符串。包含则返回池中的字符串,否则将字符串对象添加到池中,并返回该字符串对象的引用
        //(最终结果就是返回一个指向常量池字符串的引用)
        System.out.println(hello == hello2.intern());   //true
        //true. 两个都是常量表达式(编译时确定的),都是指向字符串池的引用
        System.out.println(hello == "Hello");           //true
        //true. 不同类下的两个常量表达式,依然是指向同一个字符串池的引用
        System.out.println(Other.hello == hello);       //true
        //true. 不同包下的两个常量表达式,依然是指向同一个字符串池的引用
        //System.out.println(com.example.base.string.other.Other.hello == hello);     //true
        //true. 两个都是常量表达式,都是指向字符串池的引用
        System.out.println(hello == ("Hel" + "lo"));    //true
        //false.  后者不是常量表达式,是运行时通过串联计算的字符串(lo是一个对象,不是常亮"xxx"),会新建对象
        System.out.println(hello == ("Hel" + lo));      //false
        //true. 将新对象intern()会返回指向字符串池的引用
        System.out.println(hello == ("Hel" + lo).intern());     //true
    }
}
class Other {
    static String hello = "Hello";
}

StringBuffer 中toStringCache的作用

StringBuffer 调用了 String(char[] value, boolean share) 来构造一个 String 对象,它传入了一个字符数组,也就是缓存 toStringCache。Java 中定义,一个 String 对象是常量,是不能被改变的,因此 StringBuffer 的 toString() 返回的对象也应该是不能被改变。也就意味着传入的 toStringCache 数组的元素的值也不能被改变,否则由它构造的 String 对象就会改变。

如下,我们通过反射调用 String(char[] value, boolean share) 构造处一个字符串对象,然后修改 value 数组的值,会发现字符串对象发生了改变。

class ToStringCacheTest {

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Constructor<String> constructor = String.class.getDeclaredConstructor(char[].class, boolean.class);
        constructor.setAccessible(true);

        char[] value = new char[]{'R', 'o', 'b', 'o', 't', 'h', 'y'};

        String str = constructor.newInstance(value, true);

        System.out.println(str); // 输出:Robothy
        value[0] = 'A';          // 修改 str 绑定的字符数组 value
        System.out.println(str); // 输出:Aobothy
    }

}

StringBuffer 中的 toStringCache 是字符数组 value 复制的一个副本,每当 value 发生改变时,toStringCache 都会被置为空。这就保证了每次只要 StringBuffer 对象发生改变,再调用 toString() 方法就必然产生一个新的 toStringCache 数组,从而保证了引用了旧的 toStringCache 的字符串对象不会发生改变。即使多个线程同时访问 StringBuffer 对象,某一时刻也只有一个线程能够进入修改 toStringCache 和 value 的代码块,这通过修饰 StringBuffer 方法的 synchroinzed 关键字来保证。

StringBuffer 调用了 String(char[] value, boolean share) 来构造一个 String 对象,它传入了一个字符数组,也就是缓存 c。Java 中定义,一个 String 对象是常量,是不能被改变的,因此 StringBuffer 的 toString() 返回的对象也应该是不能被改变。也就意味着传入的 toStringCache 数组的元素的值也不能被改变,否则由它构造的 String 对象就会改变。

如下,我们通过反射调用 String(char[] value, boolean share) 构造处一个字符串对象,然后修改 value 数组的值,会发现字符串对象发生了改变。

class ToStringCacheTest {

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Constructor<String> constructor = String.class.getDeclaredConstructor(char[].class, boolean.class);
        constructor.setAccessible(true);

        char[] value = new char[]{'R', 'o', 'b', 'o', 't', 'h', 'y'};

        String str = constructor.newInstance(value, true);

        System.out.println(str); // 输出:Robothy
        value[0] = 'A';          // 修改 str 绑定的字符数组 value
        System.out.println(str); // 输出:Aobothy
    }

}

StringBuffer 中的 toStringCache 是字符数组 value 复制的一个副本,每当 value 发生改变时,toStringCache 都会被置为空。这就保证了每次只要 StringBuffer 对象发生改变,再调用 toString() 方法就必然产生一个新的 toStringCache 数组,从而保证了引用了旧的 toStringCache 的字符串对象不会发生改变。即使多个线程同时访问 StringBuffer 对象,某一时刻也只有一个线程能够进入修改 toStringCache 和 value 的代码块,这通过修饰 StringBuffer 方法的 synchroinzed 关键字来保证。

引用:https://www.cnblogs.com/robothy/p/13895241.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
StringStringBufferStringBuilder都是Java用于处理字符串的类。 String是一个不可变的字符串类,也就是说一旦创建了一个String对象,它的值就不能被修改。每次对String进行修改操作时,都会创建一个新的String对象,这样会浪费内存空间和时间。因此,当需要频繁地对字符串进行修改时,使用String并不高效。 StringBufferStringBuilder是可变的字符串类,它们可以被用来进行字符串的修改操作。StringBufferStringBuilder的主要区别在于StringBuffer是线程安全的,而StringBuilder是非线程安全的。这意味着在多线程环境下,如果有多个线程同时访问一个StringBuffer对象,它们是安全的;而多个线程同时访问一个StringBuilder对象时,可能会导致数据错乱。 使用StringBufferStringBuilder的场景通常是在需要频繁地对字符串进行修改的情况下。例如,在循环拼接字符串、在递归函数修改字符串等情况下,使用StringBufferStringBuilder可以提高性能。 如果需要将StringBufferStringBuilder转换为String对象,可以使用两种方式。一种是调用它们的toString()方法,将其转换为String对象。另一种是使用String的构造器String(StringBuffer buffer)来创建一个新的String对象,将StringBufferStringBuilder的内容复制到新的String对象。 总结起来,String是不可变的字符串类,而StringBufferStringBuilder是可变的字符串类,适用于需要频繁修改字符串的场景。转换为String对象可以通过调用toString()方法或使用String的构造器来实现。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值