String、StringBuffer和StringBuilder的爱恨纠缠

String类,StringBuffer 和 StringBuilder 的爱恨纠缠

  • 开始
    • 一、底层
    • 二、三者区别
    • 三、总结

开始

本文主要通过源码来展开String类,StringBuffer 和 StringBuilder三者的区别

一、底层

我们知道String的储值单位是final修饰的字符数组,所有String是不可变的(不晓得的同学请戳此文:),每次对String的操作都会生成新的String对象,效率降低还造成内存浪费
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cDyE7FUl-1590462091074)(C:\Users\13798\Desktop\图片\String value.png)]
而StringBuffer和StringBuilder就不一样了,他们两都继承了AbstractStringBuilder抽象类,从AbstractStringBuilder抽象类中我们可以看到并没有final修饰符
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ta97RwPT-1590462091078)(C:\Users\13798\Desktop\图片\Sb value.png)]
我们他们的继承结构及源码可知,三者的底层都是可变的字符数组
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-szihgA2Q-1590462091081)(C:\Users\13798\Desktop\图片\String Sb)]
而StringBuffer的许多方法是通过StringBuilder实现的,且StringBuffer方法是由synchronized修饰的,由此可得StringBuffer是线程安全的,而StringBuilder不是线程安全的,而StringBuilder的速度比StringBuffer快
以StringBuilder和StringBuffer的append方法为例:
StringBuilder:

    @Override
    public StringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }

    @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }

StringBuffer:

    @Override
    public synchronized StringBuffer append(Object obj) {
        toStringCache = null;
        super.append(String.valueOf(obj));
        return this;
    }

    @Override
    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }

二、三者区别

###1.可变与不可变
String类是一个不可变类,即创建String对象后,该对象中的字符串是不可改变的,直到这个对象被销毁。StringBuffer与StringBuilder都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,是可变类。

由于String是可变类,适合在需要被共享的场合中使用,当一个字符串经常被修改时,最好使用StringBuffer实现。如果用String保存一个经常被修改的字符串,该字符串每次修改时都会创建新的无用的对象,这些无用的对象会被垃圾回收器回收,会影响程序的性能,不建议这么做。

###2.初始化方式
当创建String对象时,可以利用构造方法String str = new String(“Java”)的方式来对其进行初始化,也可以直接用赋值的方式String s = "Java"来初始化。而StringBuffer只能使用构造方法StringBuffer sb = new StringBuffer(“hello”)的方式初始化。

###3.字符串修改方式
String的concat方法和’+'操作
concat源码:

    public String concat(String str) {
        int otherLen = str.length();
        if (otherLen == 0) {
            return this;
        }
        int len = value.length;
        char buf[] = Arrays.copyOf(value, len + otherLen);
        str.getChars(buf, len);
        return new String(buf, true);
    }

源码中对String中+操作符的描述如下:

The Java language provides special support for the string concatenation operator ( + ), and for conversion of other objects to strings. String concatenation is implemented through the StringBuilder(or StringBuffer) class and its append method.

简单的概括下:String本身是不变的对象,但是string的+号操作符是通过StringBuilder或StringBuffer(可以通过反汇编class文件,看到使用的StringBuilder来实现的。)

以上String的两个方法中都有开辟(new)以及销毁堆空间的操作,大量的String操作导致效率很低。所以在大量操作字符串时StringBuffer和StringBuilder是最好的选择。
###4.是否实现了equals和hashCode方法
String实现了equals()方法和hashCode()方法,new String(“java”).equals(new String(“java”))的结果为true;

而StringBuffer没有实现equals()方法和hashCode()方法,因此,new StringBuffer(“java”).equals(new StringBuffer(“java”))的结果为false,将StringBuffer对象存储进Java集合类中会出现问题。
我们用一个测试方法来试验一下:

	@Test
	public void testString(){
		StringBuffer stringBuffer1 = new StringBuffer("a");
		StringBuffer stringBuffer2 = new StringBuffer("a");
		StringBuffer stringBuffer3 = new StringBuffer("b");
		String s1=new String("a");
		String s2=new String("a");
		String s3=new String("b");
		HashSet hsb=new HashSet();
		hsb.add(stringBuffer1);
		hsb.add(stringBuffer2);
		hsb.add(stringBuffer3);
		System.out.println(""+hsb);
		HashSet hs=new HashSet();
		hs.add(s1);
		hs.add(s2);
		hs.add(s3);
		System.out.println(""+hs);
	}

运行结果:
hsb: [a, a, b]
hs :[a, b]
从结果可以看出对于拥有相同值的StringBuffer类型的数据,HashSet没有去重,但是对于拥有相同值的String的类型的数据,HashSet便去重了。
StringBuffer和StringBuilder值的比较可以使用.toString()方法将内容转化为String字符串,再用.equals()方法比较
如: new StringBuffer(“a”).toString().equalsIgnoreCase(new StringBuffer(“a”).toString())
###5.是否线程安全
StringBuffer与StringBuilder都提供了一系列插入、追加、改变字符串里的字符序列的方法,它们的用法基本相同,只是StringBuilder是线程不安全的,StringBuffer是线程安全的(StringBuffer方法是由synchronized修饰的)。如果只是在单线程中使用字符串,则StringBuilder的效率会高些,但是当多线程访问时,最好使用StringBuffer。

三、总结

综上,在执行效率方面,StringBuilder最高,StringBuffer次之,String最低,对于这种情况,一般而言,如果要操作的数量比较小,应优先使用String类;如果是在单线程下操作大量数据,应优先使用StringBuilder类;如果是在多线程下操作大量数据,应优先使用StringBuilder类。

如有不足之处,敬请指教!

参考文章:
https://cloud.tencent.com/developer/article/1414756
https://blog.csdn.net/TTTZZZTTTZZZ/article/details/84892985
https://www.iteye.com/problems/41712

你可能感兴趣的:(Java知识链,java,字符串)