声明:本文大部分内容为参考别人的,自己简单汇总整理了一下。
String 字符串常量
StringBuffer 字符串变量(线程安全)
StringBuilder 字符串变量(非线程安全)
简要的说, String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因为String类的声明是:public final,因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度是一定会相当慢的。
在大部分情况下 StringBuffer > String
Java.lang.StringBuffer:线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。
可将字符串缓冲区安全地用于多个线程。可以在必要时对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。
StringBuffer上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的位置添加字符。
例如,如果 z 引用一个当前内容是“start”的字符串缓冲区对象,则此方法调用 z.append(“le”) 会使字符串缓冲区包含“startle”,而 z.insert(4, “ab”) 将更改字符串缓冲区,使之变为“starabtle”。
在大部分情况下 StringBuilder > StringBuffer
java.lang.StringBuilder:一个可变的字符序列,是5.0新增的。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。两者的方法基本相同。
Java中StringBuilder的用法:
String对象是不可改变的。每次使用 System.String类中的方法之一时,都要在内存中创建一个新的字符串对象,这就需要为该新对象分配新的空间。在需要对字符串执行重复修改的情况下,与创建新的 String对象相关的系统开销可能会非常昂贵。如果要修改字符串而不创建新的对象,则可以使用java.lang.StringBuilder类。例如,当在一个循环中将许多字符串连接在一起时,使用 StringBuilder类可以提升性能。
(一)通过用一个重载的构造函数方法初始化变量,可以创建 StringBuilder类的新实例,比如:
StringBuilder testStringBuilder = new StringBuilder("Hello World!");
(二)设置容量和长度:
虽然 StringBuilder对象是动态对象,允许扩充它所封装的字符串中字符的数量,但是您可以为它可容纳的最大字符数指定一个值。此值称为该对象的容量,不应将它与当前 StringBuilder对象容纳的字符串长度混淆在一起。例如,可以创建 StringBuilder类的带有字符串“Hello”(长度为 5)的一个新实例,同时可以指定该对象的最大容量为 25。当修改 StringBuilder时,在达到容量之前,它不会为其自己重新分配空间。当达到容量时,将自动分配新的空间且容量翻倍 int newCapacity = value.length * 2 + 2;。可以使用重载的构造函数之一来指定 StringBuilder类的容量。比如:以下代码可以将 testStringBuilder对象扩充到最大25个空白
StringBuilder testStringBuilder = new StringBuilder("Hello World!",25);
(三)Append方法:
将文本或对象的字符串表示形式添加到由当前 StringBuilder对象表示的字符串的结尾处。
(四)Insert方法:
将字符串或对象添加到当前 StringBuilder中的指定位置。
(五)Remove方法:
从当前 StringBuilder中移除指定数量的字符,移除过程从指定的索引处开始。
(六)Replace方法:
可以用另一个指定的字符来替换 StringBuilder对象内的字符。
如果程序对附加字符串的需求很频繁,不建议使用+来进行字符串的串联。可以考虑使用java.lang.StringBuilder 类,使用这个类所产生的对象默认会有16个字符的容量(capacity),您也可以自行指定初始容量。如果附加的字符超出可容纳的长度,则StringBuilder 对象会自动增加容量以容纳被附加的字符。如果有频繁作字符串附加的需求,使用StringBuilder 类能使效率大大提高。
见下面测试案例:
public class testStringBuilder {
public static void main(String[] args) {
String text = "";
long beginTime = System.currentTimeMillis();
for (int i = 0; i < 10000; i++)
text = text + i;
long endTime = System.currentTimeMillis();
System.out.println("普通字符串直接相加的执行时间:" + (endTime - beginTime));
StringBuffer sbf = new StringBuffer();
beginTime = System.currentTimeMillis();
for (int i = 0; i < 10000; i++)
sbf.append(String.valueOf(i));
endTime = System.currentTimeMillis();
System.out.println("使用StringBuffer的append()方法的执行时间:"+ (endTime - beginTime));
StringBuilder sb = new StringBuilder();
beginTime = System.currentTimeMillis();
for (int i = 0; i < 10000; i++)
sb.append(String.valueOf(i));
endTime = System.currentTimeMillis();
System.out.println("使用StringBuilder的append()方法的执行时间:"+ (endTime - beginTime));
}
}
输出结果:
普通字符串直接相加的执行时间:261
使用StringBuffer的append()方法的执行时间:3
使用StringBuilder的append()方法的执行时间:2
补充:Java中为什么StringBuilder比StringBuffer快?###
首先我们看一下源码:
java.lang.StringBuilder
public StringBuilder append(String str) {
super.append(str);
return this;
}
java.lang.StringBuffer
public synchronized StringBuffer append(String str) {
super.append(str);
return this;
}
可以看到,StringBuffer类的append()方法,都被synchronized所修饰,这就是我们的主角。
synchronized关键字是做什么的呢?在多线程中非常常见,根据用法不同,可以给方法、代码块或对象加锁。以此处为例,当多个线程访问加锁的方法时,每次只有一个线程获得锁,其他线程必须等待该线程释放锁(一般是执行完这个方法),再由下一个线程获得锁,才允许执行该方法。加锁会带来性能上的损耗(毕竟多执行了一些操作,还包括锁等待时的线程阻塞)。
为什么StringBuffer要加锁?StringBuffer就是为了多线程而设计的,这样可以防止多个线程同时操作同一个对象,也就是线程安全。而如果使用多线程操作StringBuilder对象,则无法保证每次append进去的字符串保持原样(随线程数量和数据量增大,出现错乱的概率也越大,跟线程切换有关)。
综上所述,Stringbuilder要比Stringbuffer快。
Question:So,什么时候使用StringBuffer呢?或者StringBuffer的适用场景是什么呢?
stringbuffer固然是线程安全的,stringbuffer固然是比stringbuilder更慢,固然,在多线程的情况下,理论上是应该使用线程安全的stringbuffer的。然而,然而,然而,有谁给我一个实际的案例来显示你需要一个线程安全的string拼接器?
对不起,至少在我浅薄的几年编程生涯中还没有遇到过,也许,仅仅是也许,这个地球上的确是存在这样的编程需求的,然而,它至少跟99.99…99%的程序员是无关的。所以,对于题主的问题,他们的适用场景是什么?
最简单的回答是,stringbuffer基本没有适用场景,你应该在所有的情况下选择使用stringbuiler,除非你真的遇到了一个需要线程安全的场景,如果遇到了,请务必在这里留言通知我。然后,补充一点,关于线程安全,即使你真的遇到了这样的场景,很不幸的是,恐怕你仍然有99.99…99%的情况下没有必要选择stringbuffer,因为stringbuffer的线程安全,仅仅是保证jvm不抛出异常顺利的往下执行而已,它可不保证逻辑正确和调用顺序正确。
大多数时候,我们需要的不仅仅是线程安全,而是锁。最后,为什么会有stringbuffer的存在,如果真的没有价值,为什么jdk会提供这个类?答案太简单了,因为最早是没有stringbuilder的,sun的人不知处于何种愚蠢的考虑,决定让stringbuffer是线程安全的,然后大约10年之后,人们终于意识到这是一个多么愚蠢的决定,意识到在这10年之中这个愚蠢的决定为java运行速度慢这样的流言贡献了多大的力量,于是,在jdk1.5的时候,终于决定提供一个非线程安全的stringbuffer实现,并命名为stringbuilder。顺便,javac好像大概也是从这个版本开始,把所有用加号连接的string运算都隐式的改写成stringbuilder。
如果没有循环的情况下,单行用加号拼接字符串是没有性能损失的,java编译器会隐式的替换成stringbuilder,但在有循环的情况下,编译器没法做到足够智能的替换,仍然会有不必要的性能损耗,因此,用循环拼接字符串的时候,还是老老实实的用stringbuilder吧。
String s1 = "aaaaa";
String s2 = "bbbbb";
String r = null;
int i = 3694;
r = s1 + i + s2;
通过查看字节码可以发现,上述代码中,JVM内部使用的是创建了一个StringBuilder完成拼接工作!!
------至所有正在努力奋斗的程序猿们!加油!!
有码走遍天下 无码寸步难行
1024 - 梦想,永不止步!
爱编程 不爱Bug
爱加班 不爱黑眼圈
固执 但不偏执
疯狂 但不疯癫
生活里的菜鸟
工作中的大神
身怀宝藏,一心憧憬星辰大海
追求极致,目标始于高山之巅
一群怀揣好奇,梦想改变世界的孩子
一群追日逐浪,正在改变世界的极客
你们用最美的语言,诠释着科技的力量
你们用极速的创新,引领着时代的变迁
——乐于分享,共同进步,欢迎补充
——Treat Warnings As Errors
——Any comments greatly appreciated
——Talking is cheap, show me the code
——诚心欢迎各位交流讨论!QQ:1138517609
——优快云:https://blog.youkuaiyun.com/u011489043
——简书:https://www.jianshu.com/u/4968682d58d1
——GitHub:https://github.com/selfconzrr