String、StringBuffer、StringBuilder之间的关系

在Java中,我们有三种存储字符串的方式:String、StringBuffer、StringBuilder。这篇文章主要是整理一下他们各自的优缺点和使用场景。该问题也是面试中常常被问到的基础知识。

它们之间的关系图如下:

image

1.三者区别

1.1 是否可变

String定义的字符串不可被改变。 每次对String对象进行改变的时候,其实是Java新创建了一个对象,然后将之前的指针指向的新的对象。这样做不仅浪费了很多空间,还降低了效率。
而StringBuilder和StringBuffer是可变对象。它们都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串。因此他们的类创建的字符串对象能够被多次修改,而不产生新的未使用对象。他们俩可使用的方法也是相同的。

1.2 线程安全

StringBuilder是线程不安全的,StringBuffer是线程安全的。因此单线程下使用StringBuilder会效率更高,多线程下使用StringBuffer会效率更高。操作小数据的话,还是String效率高。

1.3 应用场景

String使用场景:

  • 字符串内容不长发生改变的业务场景
  • 比如:常量声明、少量字符串拼接

StringBuffer应用场景:

  • 频繁进行字符串运算时(拼接、替换、删除)
  • 多线程时、因为它支持线程安全
  • 比如:XML解析、HTTP参数的解析与封装

StringBuilder应用场景:

  • 频繁进行字符串运算时(拼接、替换、删除)
  • 单线程时,因为它不支持线程安全
  • 比如:SQL语句拼接、Json数据封装

2.StringBuffer

了解StringBuffer之前,我们先回顾一下String的特点,String字符串一旦定义就不能修改也不能扩充的,而且,当使用连接符对字符串进行拼接的时候java会为字符串新开辟空间,这样就造成了空间的浪费。

为此StringBuffer作为解决方案应运而生,他是线程安全的可变字符序列。他是一个用final修饰的类,它继承于Object,实现了两个接口。该接口定义后内容可变,大小可变,使用拼接不会产生新的空间。他就类似于一个字符串的缓冲区。

2.1 容量和长度

StringBuffer有两个属性,一个是容量,一个是当前长度。容量代表的是能容纳多大的字符串,默认是16个字符大小。

2.2 构造方法

它有三个构造方法,一个是无参的(最常用),一个是传入整型参数的,用于指定容量,指定了多少,容量就变成多少。一个是字符串参数的,用于指定内容,容量是16+参数的长度。

new StringBuffer(); //容量为16
new StringBuffer(int capacity); //容量是int
new StringBuffer(String str);   //容量是str.length()+16

2.3 常用方法

  • append:追加

可以将任意的类型的数据添加到字符串的缓冲区,返回值是StringBuffer,就是它缓冲区本身。

StringBuffer sb = new StringBuffer();
StringBuffer sb1 = sb.append("hello");
System.out.println(sb); //输出hello
System.out.println(sb1); //输出hello
System.out.println(sb == sb1); //输出true

因此,直接sb.append()即可,无需使用引用进行接收。

StringBuffer sb = new StringBuffer();
sb.append(true).append(123).append("heihei");   //链式编程
System.out.println(sb);                         //输出true123heihei
  • insert:插入

此方法可指定位置插入任意类型数据。从0开始计数。

sb.insert(5,"world");
  • deleteCharAt:删除指定位置的一个字符
public StringBuffer deleteCharAt(int index);
  • delete(int start, int end ):删除指定长度字符串
public StringBuffer delete(int start, int end);
  • replace(int start, int end, String str):替换指定位置、长度字符串
public StringBuffer replace(int start, int end, String str);
  • reverse():翻转
public StringBuffer reverse();
  • 字符串截取
public String substring(int start); //从start开始截取直到最后
pubic String substring(int start, int end); //从start截取,到end结束

注意返回的是字符串

2.4 String到StringBuffer的转换

错误转换方式:

StringBuffer sb = "hello";
Stringbuffer sb = s;

正确的方式:

StringBuffer sb = new StringBuffer(s);
StringBuffer sb1 = new Stringbuffer();
sb1.append(s)

2.5 StringBuffer到String的转换

1.通过构造方法

String str = new String(buffer)

2.通过toString()

String str = buffer.toString()

任何引用类型调用toString都可以转换成字符串。

2.6 String与StringBuffer作为形参传递的不同

对于基本数据类型来说,形参的改变不影响实参,但是对于引用数据类型来说,形参的改变直接改变了实际的参数。

但是字符串虽然是引用类型,但他是比较特殊的,因为它是常量,存储在字符串常量池中。是一种特殊的引用类型,我们可以将它是为基本数据类型,所以当字符串作为形参的时候,形参改变但实参是不改变的。

StringBuffer是正常的引用类型,形参改变则实参改变。

3.可变原理

StringBuffer(始于 JDK 1.0 )和StringBuilder(始于 JDK 1.5)都是为了提高字符串的拼接效率,直接使用String的+进行拼接的话JVM会创建多个字符串对象,造成开销浪费。他俩用法一样,只不过Buffer是线程安全,Builder是线程不安全的。

String源码中有一个成员是,使用了final修饰,意思是不能更改:

private final char value[];

而Buffer和Builder的成员同样也是一个字符数组,但是没有使用final修饰:

char value[];

所以这就是可变和不可变的基础和前提。

当要存入字符串的长度+value的长度-value的长度>0的时候,说明,此时的容量已经不足以装下新的字符串了。然后Java会将原来的字符数组复制一份(使用的是str.getchars()),然后开辟一个新的数组,赋予新的容量,该容量是str.length()+value,最后return this,返回当前对象。返回当前对象的好处就是可以写成链式编程。
String只要已添加就去开辟新对象,而Buffer和Builder只有在容量不够的时候才去开辟新对象。

4.注意事项

StringBuilder sb1 = new StringBuilder("abc");
StringBuilder sb2 = new StringBuilder("abc");

System.out.println(sb1 == sb2);//false
System.out.println(sb1.equals(sb2));//false

第一个返回的是false是因为它比较的是对象的地址值,而第二个返回的是false是因为StringBuilder没有重写equals方法,使用的是Object的比较方式,也就是比较地址值。所以,要想用值来比较,应该重写equals方法。

String str1 = "abc";
StringBuilder sb1 = new StringBuilder("abc");
System.out.println(str1.equals(sb1));//false

虽然string重写了equals方法,但是,sb1对象不是String类的实例,所以返回的也是false.

参考:

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,186评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,858评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,620评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,888评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,009评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,149评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,204评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,956评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,385评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,698评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,863评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,544评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,185评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,899评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,141评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,684评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,750评论 2 351

推荐阅读更多精彩内容