Java基础(12):可变字符串—StringBuffer与StringBuilder的异同点

String是不可变类,String对象都是常量,就像“ABC”、“SDF”和“字符串123”等都是存储在常量池中。所以对String进行任何更改操作都会产生新的String对象。而StringBuilder与StringBuffer 是可变类,它们的对象是一个变量,对StringBuilder和StringBuffer对象的操作不会生成新的对象,依然是原来的对象。可以理解为String类是不可变字符串,而StringBuilder 和StringBuffer是可变字符串。

String与StringBuilder、StringBuffer创建字符串的方式:

String s1 = "字符串"; 
String s2 = new String("字符串");
		
StringBuilder sbi = new StringBuilder("字符串");
StringBuffer  sbu = new StringBuffer("字符串");

 代码可以看出String类可以使用两种方式来创建,但通常推荐第一种,因为只会生成一个String对象,而采用newString(“字符串”)的话会生成两个String对象,开销比第一种大。但遇到需要将指定类型数组转成字符串时,可以用new String()的重载构造器轻松完成,所以根据具体情况来使用。StringBuilder和StringBuffer 是可变的,只能采用new StringBuilder() 和 new StringBuffer()来创建对应的对象。 

System.out.println(s1.equals(s2)); //true
System.out.println(sbi.equals(sbu)); //false
因为String类重写了equals(),调用equals()是比较s1和s2的内容,所以返回true。而StringBuilder和StringBuffer的equals()并未重写,和==操作符一样只能比较它们的内存地址是否一致,不能比较其内存空间的内容。所以返回false。

测试StringBuffer和StringBuilder对象的哈希值:

StringBuilder sbd = new StringBuilder("字符串");
System.out.println("sbd的hashcode():" + sbd.hashCode());	
sbd = new StringBuilder("字符串"); //sbd重新指向一个new StringBuilder("字符串")		 
System.out.println("sbd的hashcode():" + sbd.hashCode());
		 
		
StringBuffer sbu = new StringBuffer("字符串");
System.out.println("sbu的hashcode():" +sbu.hashCode());	
sbu = new StringBuffer("字符串"); //sbu重新指向一个new StringBuffer("字符串")		 
System.out.println("sbu的hashcode():" +sbu.hashCode());
//		 sbd的hashcode():366712642
//		 sbd的hashcode():1829164700
//		 sbu的hashcode():2018699554
//		 sbu的hashcode():1311053135
		    
虽然重新指向的依然是new StringBuilder("字符串"),但输出的哈希值都被改变了,说明了StringBuilder或者StringBuffer只要new 之后,就会产生一个新对象,对象在堆中是唯一,所以它们的内存地址都是不同的,它们的equals()只能比较内存地址,所以不相等。

StringBuilder 和StringBuffer的区别:

StringBuilder没有实现线程同步,是非线程安全的。不支持并发。

StringBuffer实现了线程同步,是线程安全的,支持并发。

StringBuffer类的方法使用了synchronized来进行修饰,而StringBuilder的方法都未使用。

贴上一段它们内部的源代码:

//StingBuffer
 @Override
    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }
   @Override
    public synchronized StringBuffer insert(int offset, char[] str) {
        toStringCache = null;
        super.insert(offset, str);
        return this;
    }
//StringBuilder
  @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }
  @Override
    public StringBuilder deleteCharAt(int index) {
        super.deleteCharAt(index);
        return this;
    }

关于synchronized关键字,简单说一下。在Java中,synchronized表示同步,方法被修饰后就是同步方法,而同步锁就是调用该方法的当前对象,所以修饰方法无需显式指定同步锁对象。synchronized也可以作为一个代码块,把需要同步的代码放入其中,但需要显式指定同步锁对象。除了synchronized,我们还可以使用Lock来进行同步加锁,这里只是复习一下。

StringBuilder和StringBuffer使用场景:

两者的方法都是一样的,本质上是同步与不同步的区别,所以我们根据线程环境来确定使用哪一个。

在单线程环境下,无需担心线程安全问题,优先使用StringBuilder,因为效率高于StringBuffer。

在多线程环境下,则优先使用StringBuffer,保证线程安全。这会导致效率会低一点。

使用范例:

在JDBC编程中,会大量使用拼接字符串。如果我们使用String,每当使用一次,就会产生一个新String对象。当常量池中的无引用String对象越来越多时,JVM就会通知gc进行回收,这会导致我们的程序效率变低。而使用StringBuilder或StringBuffer的话只需要一个对象,大大节省了内存空间。

在下面代码中,同样是将数组转成字符串,但采用StringBuilder和String两种形式,毫无疑问是StringBuilder更加效率。需要使用拼接字符串的时候,StringBuilder和StringBuffer会更具有优势。

	//使用StringBuilder将一个数组转成字符串
	public static String arrToSB(int[] arr) {
		StringBuilder sb = new StringBuilder();
		sb.append("【"); //附加【字符串
		//遍历数组元素,添加到可变字符串sb中
		for(int i = 0; i < arr.length; i++) {
			sb.append(arr[i] + ",");
			if(i == arr.length - 1) {
				sb.append(arr[i] + "】");
			}
		}
		return sb.toString();
	}
	
	//使用String将一个数组转成字符串
	public static String arrToString(int[] arr) {
			String s = ("{"); //附加【字符串
			//遍历数组元素,添加到可变字符串sb中
			for(int i = 0; i < arr.length; i++) {
				s = s + (arr[i] + ",");
				System.out.println(s); //打印添加的过程
				if(i == arr.length - 1) {
					s = s + (arr[i] + "}");
				}
			}
			return s;
	}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值