String、StringBuffer与StringBuilder
String
String 类是不可变类,即一旦一个String对象被创建以后,包含在这个对象中的字符序列是不可改变的,直至这个对象被销毁。
例如:
String a = "123";
a = "456";
System.out.println(a) // 打印出来的a为456
咦,这不是明明已经对他进行修改了吗?为什么还说他是一个不可变类呢?接下来就看一张上述a对象的内存存储空间图
可以看出,再次给a赋值时,并不是对原来堆中实例对象进行重新赋值,而是生成一个新的实例对象,并且指向“456”这个字符串,a则指向最新生成的实例对象,之前的实例对象仍然存在,如果没有被再次引用,则会被垃圾回收。
String实例化的两种方式
- 通过字面量方式实例化
String str = "abc";
- 通过
new
+构造器的方式实例化
String str=new String("abc");
- 区别:
通过字面量方式为字符串赋值时,此时的字符串存储在方法区的字符串常量池中;
通过new+构造器方式实例化字符串时,字符串对象存储在堆中,但是字符串的值仍然存储在方法区的常量池中。
String字符串具有不可变性,当字符串重新赋值时,不管是对字符串进行拼接,还是调用String的replace()
方法修改指定的字符或字符串,都不会在原来的内存地址进行修改,而是重新分配新的内存地址进行赋值。
StringBuffer
StringBuffer对象代表一个字符序列可变的字符串,当一个StringBuffer被创建以后,通过StringBuffer提供的append()、insert()、reverse()、setCharAt()、setLength()
等方法可以改变这个字符串对象的字符序列。任何对它所指代的字符串的改变都不会产生新的对象。通过StringBuffer生成的字符串,可以调用toString()
方法将其转换为一个String对象。
例如:
StringBuffer b = new StringBuffer("123");
b.append("456");
System.out.println(b); // b打印结果为:123456
在看一下b对象的内存空间图:
可以看到它没有重新生成一个对象,而且在原来的对象中可以连接新的字符串。所以说StringBuffer对象是一个字符序列可变的字符串。
StringBuilder
StringBuilder类也代表可变字符串对象。实际上,StringBuilder和StringBuffer基本相似,两个类的构造器和方法也基本相同。不同的是:StringBuffer是线程安全的,而StringBuilder则没有实现线程安全功能,所以性能略高。StringBuffer是如何实现线程安全的呢?
StringBuffer类中实现的方法:
StringBuilder类中实现的方法:
可以看到,StringBuffer类中的方法都添加了synchronized
关键字,也就是给这个方法添加了一个锁,用来保证线程安全。
StringBuffer和StringBuilder的区别
-
线程安全
StringBuffer 线程安全,StringBuilder 线程不安全。因为 StringBuffer 的所有公开方法都是
synchronized
修饰的,而 StringBuilder 并没有synchronized
修饰。 -
缓冲区
StringBuffer 每次获取 toString 都会直接使用缓存区的 toStringCache 值来构造一个字符串。StringBuffer 的这个toString 方法仍然是同步的。
而 StringBuilder 则每次都需要复制一次字符数组,再构造一个字符串。
-
性能
StringBuffer 是线程安全的,它的所有公开方法都是同步的,StringBuilder 是没有对方法加锁同步的,所以StringBuilder 的性能要大于 StringBuffer。
StringBuffer 适用于用在多线程操作同一个 StringBuffer 的场景,而 StringBuilder 更适合单线程场合。
String、StringBuffer和StringBuilder的异同
相同点: 都用来代表字符串,底层都是通过char数组实现的。
不同点:
-
String类是不可变类,String对象一旦创建,其值是不能修改的,如果要修改,会重新开辟内存空间来存储修改之后的对象;而StringBuffer和StringBuilder对象的值是可以被修改的;即任何对String的改变会引发新的String对象的生成。
-
StringBuffer和StringBuilder类则是可变类,他俩的原理和操作基本相同,任何对它所指代的字符串的改变都不会产生新的对象。StringBuffer几乎所有的方法都使用synchronized实现了同步,支持并发操作,线程安全,在多线程系统中可以保证数据同步,但是效率比较低;而StringBuilder线程不安全,不支持并发操作,不能同步访问,不适合多线程中使用。但是效率比较高。
-
需要对字符串进行频繁的修改,不要使用String,否则会造成内存空间的浪费;推荐需要使用 StringBuffer 和 StringBuilder 类。当需要考虑线程安全的场景下使用 StringBuffer,如果不需要考虑线程安全,追求效率的场景下可以使用 StringBuilder。
感谢大家的耐心阅读,如有建议请私信或评论留言