String、StringBuffer、StringBuilder区别和联系

先看下这个:
8种基本类型的包装类和常量池
java中基本类型的包装类的大部分都实现了常量池技术,
即Byte,Short,Integer,Long,Character,Boolean;而两种浮点数类型的包装类Float,Double并没有实现常量池技术。另外String类也涉及到了常量池技术;

可参考:
Java常量池理解与总结
8种基本类型的包装类和常量池简单介绍

下面开始:
String 是字符串常量(线程安全);
StringBuffer(线程安全), StringBuilder(非线程安全) 是字符串变量。

String、StringBuilder、StringBuffer类定义

它们三个类都被final说明这三个都是不可被继承的类。
StringBuilder、StringBuffer这两个类都继承了AbstractStringBuilder。
	public final class String
	implements java.io.Serializable, Comparable<String>, CharSequence
	
	public final class StringBuilder
	extends AbstractStringBuilder
	implements java.io.Serializable, CharSequence
	
	
	public final class StringBuffer
	extends AbstractStringBuilder
	implements java.io.Serializable, CharSequence

String、StringBuilder、StringBuffer创建

String类中包含一个不可变的char数组用来存放字符串

    /** 这个value被用于存放char变量 */
    private final char value[];
    public String() {
        this.value = "".value;
    }

StringBuilderSpringBuffer都使用父类的构造函数进行初始化

    public StringBuilder() {
        super(16);
    }
    public StringBuffer() {
        super(16);
    }
//这里super(16)调用了父类的有参构造函数:
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }
    //char[] value = new char[capacity];创建了一个没有字符长度为16的字符型数组value

父类的构造方法,
可以看到和String类的区别是value类没有使用final类去修饰,所以在高并发下对value的操作是不安全的。

    char[] value;
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }

String、StringBuilder、StringBuffer的操作

String的添加操作只需要使用+号连接就行
StringBuilder、StringBuffer则需要使用append方法进行操作。
但是String使用+号操作,在不满足常量优化机制时,jvm进行编译过后也是使用StringBuilder去进行连接操作的。
StringBuilder、StringBuffer他们两的append方法区别就在于StringBuilder没有同步锁,而StringBuffer加了同步锁。
他们同时都是调用父类的方法进行实现。
父类的append类似于Arraylist的add,在超出一定容量之后都是要对value数组进行扩容
    @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }
    @Override
    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }

总结

大部分情况下效率StringBuilder > StringBuffer > String
StringBuilderStringBuffer在创建字符串及对其进行操作的优势在于都是使用的统一个对象,而Stringvalue是不可变的数组,所以在进行String 的相关操作的时候会产生许多临时的 String对象,再把这些对象引用过去,效率会很低下。
不过,一般做为简单的字符串传递和其它操作,只不要改变字符串内容的操作,用 String 效率会高一些

public class Demo_StringBuffer {
	public static void main(String[] args) {
		StringBuffer sb = new StringBuffer();
		StringBuffer sb2 = sb.append(true); 
		//System.out.println(sb2.toString())   //这里输出结果为true
		StringBuffer sb3 = sb.append("java");
		StringBuffer sb4 = sb.append(100);          
		//这一步结束之后sb,sb1,sb2,sb3这四个引用指向同一个对象,输出为truejava100
		
		System.out.println(sb.toString());			
		//StringBuffer类中重写了toString方法,显示的是对象中的属性值,sb.toString()后面的.toString()可以省略
		System.out.println(sb2);
		System.out.println(sb3);
		System.out.println(sb4);
	}
}
输出结果:
truejava100
truejava100
truejava100
truejava100

因为输出语句都在sb4之后,所以不会像那样一层一层地返回出来,而是全部输出都一样.这也间接地说明StringBuffer在声明和操作时只会占用一个空间节约内存空间,StringBuilder也是一样.
StringBuffer是字符串缓冲区,当new的时候是在堆内存创建了一个对象,底层是一个长度为16的字符数组.当调用添加的方法时,不会再重新创建对象,在不断向原缓冲区添加字符

StringBuffer中的常见方法

注意StringBuffer类中的一个方法:

public StringBuffer insert(int offset,String str):
*这里是在指定位置把任意类型的数据插入到字符串缓冲区里面,并返回字符串缓冲区本身
*注意这里是插入在这个索引的位置,而不是插入在这个对象原来索引位置上的字符后面

StringBuffer的其他方法

StringBuffer的替换功能
public StringBuffer replace(int start,int end,String str):
*从start开始到end用str替换,包含头,不包含尾

StringBuffer的删除功能
public StringBuffer delete(int start,int end):
*删除从指定位置开始指定位置结束的内容,并返回.本身包含头,不包含尾
可以:
StringBuffer sb = new StringBuffer();
sb.append("ilovejava");
sb.delete(0, sb.length()  		//这样清空缓冲区

StringBuffer的截取功能
public String substring(int start,int end):
*截取从指定位置开始到结束位置,包括开始位置,不包括结束位置
这个方法源码如下:
@Override
    public synchronized String substring(int start, int end) {
        return super.substring(start, end);
    }
所以注意截取方法的返回值类型不再是StringBuffer本身,而是String

String和StringBuffer作为参数传递的问题

首先,基本数据类型的值传递,不改变其值;而引用数据类型的值传递,改变其值
但是String类虽然是引用数据类型,但是他当作参数传递时和基本数据类型是一样的,代码如下:
public class Test {
	public static void main(String[] args) {
		String s = "Java";
		System.out.println(s);
		change(s);
		System.out.println(s);
	
		System.out.println("---------------------");
		StringBuffer sb = new StringBuffer();
		sb.append("Java");    //在调用append方法时,会产生一个地址值
		System.out.println(sb);
		change(sb);           //两个引用指向同一个对象(两个地址值给同一个对象)
		System.out.println(sb);
	}

	public static void change(StringBuffer sb) {
		sb.append("Script");
	}

	public static void change(String s) {
		s += "Script";
	}

}
输出结果:
Java
Java
---------------------
Java
JavaScript

这也说明了String对象是不可变的
另外关于java基本数据类型传递与引用传递区别可以参考:
java基本数据类型传递与引用传递区别详解

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值