全面解释java中StringBuilder、StringBuffer、String类之间的关系

时间:2021-11-23 07:37:28

StringBuilder、StringBuffer、String类之间的关系

  java中String、StringBuffer、StringBuilder是编程中经常使用的字符串类,在上一篇博文中我们已经熟悉String字符串的特性和使用,而StringBuffer、StringBuilder又是怎么样的字符串类呢??他们之间的区别和关系又是什么呢??这问题经常在面试中会问到,现在总结一下,看看他们的不同与相同。

1.可变与不可变

1)String类中使用字符数组保存字符串,如下就是,因为有“final”修饰符,所以可以知道string对象是不可变的。

    private final char value[];

String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且大量浪费有限的内存空间。

 String a = "a"; //假设a指向地址0x0001
a = "b";//重新赋值后a指向地址0x0002,但0x0001地址中保存的"a"依旧存在,但已经不再是a所指向的,a 已经指向了其它地址。

因此String的操作都是改变赋值地址而不是改变值操作。

2)StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,如下就是,可知这两种对象都是可变的。

    char[] value;

StringBuffer是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。 每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量。

 StringBuffer buf=new StringBuffer(); //分配长16字节的字符缓冲区
StringBuffer buf=new StringBuffer(512); //分配长512字节的字符缓冲区
StringBuffer buf=new StringBuffer("this is a test")//在缓冲区中存放了字符串,并在后面预留了16字节的空缓冲区。

StringBuffer和StringBuilder类功能基本相似,主要区别在于StringBuffer类的方法是多线程、安全的,而StringBuilder不是线程安全的,相比而言,StringBuilder类会略微快一点。对于经常要改变值的字符串应该使用StringBuffer和StringBuilder类。

2.是否多线程安全

String中的对象是不可变的,也就可以理解为常量,显然线程安全

AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。

StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。看如下源码:

 public synchronized StringBuffer reverse() {
super.reverse();
return this;
} public int indexOf(String str) {
return indexOf(str, 0); //存在 public synchronized int indexOf(String str, int fromIndex) 方法
}

StringBuilder并没有对方法进行加同步锁,所以是非线程安全的

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

StringBuffer是线程安全的,这意味着它们已经同步方法来控制访问,以便只有一个线程可以在同一时间访问一个StringBuffer对象同步代码。因此,StringBuffer的对象通常在多线程环境中是安全的,使用多个线程可以试图同时访问相同StringBuffer对象。

StringBuilder类非常相似的StringBuffer,不同之处在于它的访问不同步的,因此,它不是线程安全的。由于不同步,StringBuilder的性能可以比StringBuffer更好。因此,如果在单线程环境中工作,使用StringBuilder,而不是StringBuffer可能会有更高的性能。这也类似其他情况,如StringBuilder的局部变量(即一个方法中的一个变量),其中只有一个线程会访问一个StringBuilder对象。

3.StringBuffer和StringBuilder类的速度比较

一般情况下,速度从快到慢:StringBuilder>StringBuffer>String,这种比较是相对的,不是绝对的。(要考虑程序是单线程还是多线程)

接下来,我直接贴上测试过程和结果的代码,一目了然:

 package com.hysum.test;

 public class StringTest {
final static int time = 50000; //循环次数
/*
* String类测试方法
*/
public void test(String s){
long begin = System.currentTimeMillis();//获取当前系统时间(毫秒数),开始
for(int i=0; i<time; i++){
s += "add";
}
long over = System.currentTimeMillis();//获取当前系统时间(毫秒数),结束
System.out.println("操作"+s.getClass().getName()+"类型使用的时间为:"+(over-begin)+"毫秒");
}
/*
* StringBuffer类测试方法
*/
public void test(StringBuffer s){
long begin = System.currentTimeMillis();
for(int i=0; i<time; i++){
s.append("add");
}
long over = System.currentTimeMillis();
System.out.println("操作"+s.getClass().getCanonicalName()+"类型使用的时间为:"+(over-begin)+"毫秒");
}
/*
* StringBuilder类测试方法
*/
public void test(StringBuilder s){
long begin = System.currentTimeMillis();
for(int i=0; i<time; i++){
s.append("add");
}
long over = System.currentTimeMillis();
System.out.println("操作"+s.getClass().getName()+"类型使用的时间为:"+(over-begin)+"毫秒");
} /*对 String 直接进行字符串拼接的测试*/
public void test2(){//操作字符串对象引用相加类型使用的时间
String s2 = "abcd";
long begin = System.currentTimeMillis();
for(int i=0; i<time; i++){
String s = s2 + s2 +s2;
}
long over = System.currentTimeMillis();
System.out.println("操作字符串对象引用相加类型使用的时间为:"+(over-begin)+"毫秒");
}
public void test3(){//操作字符串相加使用的时间
long begin = System.currentTimeMillis();
for(int i=0; i<time; i++){
String s = "abcd" + "abcd" + "abcd";
}
long over = System.currentTimeMillis();
System.out.println("操作字符串相加使用的时间为:"+(over-begin)+"毫秒");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
String s1 = "abcd";
StringBuffer st1 = new StringBuffer( "abcd");
StringBuilder st2 = new StringBuilder( "abcd");
StringTest tc = new StringTest();
tc.test(s1);
tc.test(st1);
tc.test(st2);
tc.test2();
tc.test3();
} }

运行结果:

全面解释java中StringBuilder、StringBuffer、String类之间的关系

结果分析:

从上面的结果可以看出,不考虑多线程,采用String对象时,执行时间比其他两个都要高得多,而采用StringBuffer对象和采用StringBuilder对象的差别也比较明显;而以String类为例,操作字符串对象引用相加类型使用的时间比直接/操作字符串相加使用的时间也多得多。由此可见,如果我们的程序是在单线程下运行,或者是不必考虑到线程同步问题,我们应该优先使用StringBuilder类;如果要保证线程安全,自然是StringBuffer;能直接操作字符串不用字符串引用就直接操作字符串。

4、StringBuilder与StringBuffer共同点

StringBuilder与StringBuffer有公共父类AbstractStringBuilder(抽象类)。

StringBuilder、StringBuffer的方法都会调用AbstractStringBuilder中的公共方法,如super.append(...)。只是StringBuffer会在方法上加synchronized关键字,进行同步。

那么我们接来下看一下它们的主要方法吧~

方法 说明
StringBuffer append(参数) 追加内容到当前StringBuffer对象的末尾,类似于字符串的连接
StringBuffer deleteCharAt(int index) 删除指定位置的字符,然后将剩余的内容形成新的字符串
StringBuffer insert(位置, 参数) 在StringBuffer对象中插入内容,然后形成新的字符串
StringBuffer reverse() 将StringBuffer对象中的内容反转,然后形成新的字符串
void setCharAt(int index, char ch) 修改对象中索引值为index位置的字符为新的字符ch
void trimToSize() 将StringBuffer对象的中存储空间缩小到和字符串长度一样的长度,减少空间的浪费,和String的trim()是一样的作用
StringBuffer delete(int start, int end) 删除指定区域的字符串
StringBuffer replace(int start, int end, String s)  用新的字符串替换指定区域的字符串
void setlength(int n) 设置字符串缓冲区大小
int capacity() 获取字符串的容量
void ensureCapacity(int n) 确保容量至少等于指定的最小值。如果当前容量小于该参数,然后分配一个新的内部数组容量更大。新的容量是较大的.
getChars(int start,int end,char chars[],int charStart); 将字符串的子字符串复制给数组

以下是各个方法的代码示例:

 public static void main(String[] args) {
// TODO Auto-generated method stub
StringBuilder str=new StringBuilder("学习 java 编程"); //增加字符串内容的方法
//append(参数),追加内容到当前对象的末尾
str.append("学习使我快乐");
System.out.println("追加内容到当前对象的末尾:"+str);
// insert(位置, 参数),在对象中插入内容
str.insert(10,',');
System.out.println("在对象中插入内容:"+str); //操作字符串内容的方法
//delete(int start, int end),删除指定区域的字符串
str.delete(11, 17);
System.out.println("删除指定区域的字符串:"+str);
//deleteCharAt(int index),删除指定位置的字符
str.deleteCharAt(10);
System.out.println("删除指定位置的字符:"+str);
//setCharAt(int index, char newChar),修改对象中索引值为index位置的字符为新的字符ch
str.setCharAt(3, 'J');
System.out.println("修改对象中索引值为index位置的字符为新的字符ch:"+str);
//replace(int start, int end, String s), 用新的字符串替换指定区域的字符串
str.replace(4, 7, "AVA");
System.out.println("用新的字符串替换指定区域的字符串:"+str);
// reverse()内容反转
str.reverse();
System.out.println("内容反转:"+str);
//将字符串的子字符串复制给数组。
char[] ch = new char[5];
str.getChars(0, 4, ch, 0);
System.out.println("将字符串的子字符串复制给数组:"+Arrays.toString(ch)); StringBuilder str2=new StringBuilder(30);//创建一个长度为30的字符串
str2.append("JAVA");
System.out.println("字符串长度为:"+str2.length());//length(),获取字符串长度
System.out.println("字符串容量为:"+str2.capacity());//capacity(),获取字符串的容量
//有关字符串空间的方法
//setLength(int newSize),设置字符串缓冲区大小
str2.setLength(20);
System.out.println("字符串长度为:"+str2.length());
System.out.println("字符串容量为:"+str2.capacity());
//ensureCapacity(int n),重新设置字符串容量的大小
str2.ensureCapacity(20);
System.out.println("字符串长度为:"+str2.length());
System.out.println("字符串容量为:"+str2.capacity());
str2.ensureCapacity(35);
System.out.println("字符串长度为:"+str2.length());
System.out.println("字符串容量为:"+str2.capacity());
//trimToSize(),存储空间缩小到和字符串长度一样的长度
str2.trimToSize();
System.out.println("字符串长度为:"+str2.length());
System.out.println("字符串容量为:"+str2.capacity()); } }

运行结果:

全面解释java中StringBuilder、StringBuffer、String类之间的关系

结果分析:

1、在使用有范围的参数方法时,要注意范围包括开头不包括结尾!

2、insert方法的位置是你要插入的位置,不是插入前一个位置!

3、getChars方法中注意字符数组的长度一定要大于等于begin到end之间字符的长度!

4、length是字符串内容的长度,而capacity是字符串容量(包括缓存区)的长度!

5、ensureCapacity方法是确保容量至少等于指定的最小值。如果当前容量小于该参数,然后分配一个新的内部数组容量更大(不是你指定的值,系统自动分配一个空间)。如果当前容量不小于该参数,则容量不变。

6、trimToSize(),存储空间缩小到和字符串长度一样的长度。避免空间的浪费

总结

(1).如果要操作少量的数据用 = String
(2).单线程操作字符串缓冲区 下操作大量数据 = StringBuilder
(3).多线程操作字符串缓冲区 下操作大量数据 = StringBuffer

参考文献:

http://www.jb51.net/article/33398.htm

http://blog.csdn.net/mad1989/article/details/26389541

全面解释java中StringBuilder、StringBuffer、String类之间的关系的更多相关文章

  1. java中StringBuilder、StringBuffer、String类之间的关系

    今天在CSDN的高校俱乐部里看到了"Java基础水平測试(英文)".感觉自己学了java这么久,想看下自己的java水平究竟是个什么样.測试结果就不说了,反正是慘不忍睹. 看了一下 ...

  2. Java中int和String类型之间转换

    int –> String int i=123; String s=""; 第一种方法:s=i+""; //会产生两个String对象 第二种方法:s=S ...

  3. 在Java中如何用String类中的indexof方法得到一个词的出现频率

    public class Test{ public static void main(String[] args) { String s="hello jack hello look me ...

  4. java中静态类与普通类之间区别

    所谓静态,指以static关键字修饰的,包括类,方法,块,字段. 非静态,指没有用static 修饰的. 一.静态类的特点 1.全局唯一,任何一次的修改都是全局性的影响 2.只加载一次,优先于非静态 ...

  5. java学习之Java中JDK&comma;JRE和JVM之间的关系(转载)

    最近要重新抓一下java,大量扫技术文档,保存下来供自己查阅.以下转载自http://www.cnblogs.com/xiaofeixiang/p/4085159.html 初学JAVA很容易被其中的 ...

  6. Java中JDK&comma;JRE和JVM之间的关系

    初学JAVA很容易被其中的很多概念弄的傻傻分不清楚,首先从概念上理解一下吧,JDK(Java Development Kit)简单理解就是Java开发工具包,JRE(Java Runtime Envi ...

  7. Java中JDK&comma;JRE和JVM之间的关系-&lpar;转载&rpar;

    初学JAVA很容易被其中的很多概念弄的傻傻分不清楚,首先从概念上理解一下吧,JDK(Java Development Kit)简单理解就是Java开发工具包,JRE(Java Runtime Envi ...

  8. pygame中多个class类之间的关系

    用一个实例介绍一下有关pygame中不同类之间的通信,详细介绍在代码段有标注,感兴趣的可以复制代码试试: import pygame import sys # -------------------- ...

  9. Java中有趣的String、StringBuffer与StringBuilder

    String介绍 String类属于java.lang包中,String类是不可变类,任何对String的改变都会引发新的String对象的生成. 创建String的两种方式: 1.通过构造器创建:S ...

随机推荐

  1. 如何开发一个Jquery插件

    Jquery有两种开发插件的方法: 1.jquery.fn.extend(object); 2.jquery.extend(object); 第一种方法是给Jquery对象添加方法,jquery.fn ...

  2. T-SQL备忘-表连接更新

    1.update a inner join b on a.id=b.id set a.name=b.name where ...   2.update table1 set a.name = b.na ...

  3. 杭电 1795 The least one

    The least one Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Tot ...

  4. bzoj1588 &lbrack;HNOI2002&rsqb;营业额统计(Treap)

    1588: [HNOI2002]营业额统计 Time Limit: 5 Sec  Memory Limit: 162 MBSubmit: 11485  Solved: 4062[Submit][Sta ...

  5. Python NTLK资料

    1.Creating a new corpus with NLTK http://*.com/questions/4951751/creating-a-new-corpus-w ...

  6. ok6410 u-boot-2012&period;04&period;01移植七完善u-boot移植&lpar;u-boot移植结束&rpar;

    继ok6410 u-boot-2012.04.01移植六后,开发板已支持MLC NAND.DM9000等.但还需要完善比如环境变量.mtdpart分区.裁剪.制作补丁等.下面的工作就是完善移植的u-b ...

  7. bat编程基本知识

    1 声明变量 ::注意=前后不要留空格.随便说一下,在bat中,连续两个冒号表示注释 set var1=test 如果要引用这个变量的话,可以这样写:%var1% 2 echo off/on echo ...

  8. 7&period;4 GRASP原则四:控制器 Controller

    4.GRASP原则四:控制器 Controller  What first object beyond the UI layer receives and co-ordinates (control ...

  9. 【Java集合系列一】ArrayList解析

    一.基础简介 1.ArrayList继承关系 2.底层用数组来存储数据,数据会在ArrayList创建的时候一并初始化.如果创建ArrayList的时候,没有设置容量,则会delay到第一次add数据 ...

  10. Java Future 和 FutureTask 源码Demo

    Future 是一个接口,看源码有Future 和 FutreTask 使用Demo package java.util.concurrent; /** * A <tt>Future&lt ...