JAVA的String、StringBuilder和StringBuffer类的区别

StringBuffer类(或者StringBuilder)和String一样,也用来代表字符串,只是由于StringBuffer的内部实现方式和String不同,所以StringBuffer在进行字符串处理时,不生成新的对象,在内存使用上要优于String类。

所以在实际使用时,如果经常需要对一个字符串进行修改,例如插入、删除等操作,使用StringBuffer要更加适合一些。

在StringBuffer类中存在很多和String类一样的方法,这些方法在功能上和String类中的功能是完全一样的。

但是有一个最显著的区别在于,对于StringBuffer对象的每次修改都会改变对象自身,这点是和String类最大的区别。

另外由于StringBuffer是线程安全的,关于线程的概念后续有专门的章节进行介绍,所以在多线程程序中也可以很方便的进行使用,但是程序的执行效率相对来说就要稍微慢一些。

1、StringBuffer对象的初始化

StringBuffer对象的初始化不像String类的初始化一样,Java提供的有特殊的语法,而通常情况下一般使用构造方法进行初始化。

例如:

     StringBuffer s = new StringBuffer();

这样初始化出的StringBuffer对象是一个空的对象。

如果需要创建带有内容的StringBuffer对象,则可以使用:

StringBuffer s = new StringBuffer(“abc”);

这样初始化出的StringBuffer对象的内容就是字符串”abc”。

需要注意的是,StringBuffer和String属于不同的类型,也不能直接进行强制类型转换,下面的代码都是错误的:

StringBuffer s = “abc”;        //赋值类型不匹配
tringBuffer s = (StringBuffer)”abc”; //不存在继承关系,无法进行强转
StringBuffer对象和String对象之间的互转的代码如下:
         String s = “abc”;
         StringBuffer sb1 = new StringBuffer(“123”);
         StringBuffer sb2 = new StringBuffer(s);   //String转换为StringBuffer
         String s1 = sb1.toString();              //StringBuffer转换为String

2、StringBuffer的常用方法

StringBuffer类中的方法主要偏重于对于字符串的变化,例如追加、插入和删除等,这个也是StringBuffer和String类的主要区别。

a、append方法

public StringBuffer append(boolean b)

该方法的作用是追加内容到当前StringBuffer对象的末尾,类似于字符串的连接。调用该方法以后,StringBuffer对象的内容也发生改变,例如:

StringBuffer sb = new StringBuffer(“abc”);

sb.append(true);

则对象sb的值将变成”abctrue”。

使用该方法进行字符串的连接,将比String更加节约内容,例如应用于数据库SQL语句的连接,例如:

StringBuffer sb = new StringBuffer();
String user = “test”;
String pwd = “123”;
sb.append(“select * from userInfo where username=“)
          .append(user)
          .append(“ and pwd=”)
          .append(pwd);

这样对象sb的值就是字符串“select * from userInfo where username=test and pwd=123”。

b、deleteCharAt方法

public StringBuffer deleteCharAt(int index)

该方法的作用是删除指定位置的字符,然后将剩余的内容形成新的字符串。例如:

StringBuffer sb = new StringBuffer(“Test”);
sb. deleteCharAt(1);

该代码的作用删除字符串对象sb中索引值为1的字符,也就是删除第二个字符,剩余的内容组成一个新的字符串。所以对象sb的值变为”Tst”。

还存在一个功能类似的delete方法:

public StringBuffer delete(int start,int end)

该方法的作用是删除指定区间以内的所有字符,包含start,不包含end索引值的区间。例如:

StringBuffer sb = new StringBuffer(“TestString”);
sb. delete (1,4);

该代码的作用是删除索引值1(包括)到索引值4(不包括)之间的所有字符,剩余的字符形成新的字符串。则对象sb的值是”TString”。

c、insert方法

public StringBuffer insert(int offset, boolean b)

该方法的作用是在StringBuffer对象中插入内容,然后形成新的字符串。例如:

StringBuffer sb = new StringBuffer(“TestString”);
sb.insert(4,false);

该示例代码的作用是在对象sb的索引值4的位置插入false值,形成新的字符串,则执行以后对象sb的值是”TestfalseString”。

d、reverse方法

public StringBuffer reverse()

该方法的作用是将StringBuffer对象中的内容反转,然后形成新的字符串。例如:

StringBuffer sb = new StringBuffer(“abc”);
sb.reverse();

转以后,对象sb中的内容将变为”cba”。

e、setCharAt方法

public void setCharAt(int index, char ch)

该方法的作用是修改对象中索引值为index位置的字符为新的字符ch。例如:

StringBuffer sb = new StringBuffer(“abc”);

sb.setCharAt(1,’D’);

则对象sb的值将变成”aDc”。

f、trimToSize方法

public void trimToSize()

该方法的作用是将StringBuffer对象的中存储空间缩小到和字符串长度一样的长度,减少空间的浪费。

总之,在实际使用时,String和StringBuffer各有优势和不足,可以根据具体的使用环境,选择对应的类型进行使用。

String和StringBuffer的效率对比

为了更加明显地看出它们的执行效率,下面的代码,将26个英文字母加了10000次。

public class Demo {
    public static void main(String[] args){
        String fragment = "abcdefghijklmnopqrstuvwxyz";
        int times = 10000;
    // 通过String对象
        long timeStart1 = System.currentTimeMillis();
        String str1 = "";
        for (int i=0; i<times; i++) {
        str1 += fragment;
        }
        long timeEnd1 = System.currentTimeMillis();
        System.out.println("String: " + (timeEnd1 - timeStart1) +     "ms");
    // 通过StringBuffer
        long timeStart2 = System.currentTimeMillis();
        StringBuffer str2 = new StringBuffer();
        for (int i=0; i<times; i++) {
            str2.append(fragment);
        }
        long timeEnd2 = System.currentTimeMillis();
        System.out.println("StringBuffer: " + (timeEnd2 - timeStart2) + "ms");
    }
}

运行结果:

String: 5287ms
StringBuffer: 3ms

结论很明显,StringBuffer的执行效率比String快上千倍,这个差异随着叠加次数的增加越来越明显,当叠加次数达到30000次的时候,运行结果为:

String: 35923ms

StringBuffer: 8ms

所以,强烈建议在涉及大量字符串操作时使用StringBuffer。

StringBuilder类

StringBuilder类和StringBuffer类功能基本相似,方法也差不多,主要区别在于StringBuffer类的方法是多线程安全的,而StringBuilder不是线程安全的,相比而言,StringBuilder类会略微快一点。

StringBuffer、StringBuilder、String中都实现了CharSequence接口。

CharSequence是一个定义字符串操作的接口,它只包括length()、charAt(int index)、subSequence(int start, int end) 这几个API。

StringBuffer、StringBuilder、String对CharSequence接口的实现过程不一样,如下图所示:

可见,String直接实现了CharSequence接口;StringBuilder 和 StringBuffer都是可变的字符序列,它们都继承于AbstractStringBuilder,实现了CharSequence接口。

jdk的实现中StringBuffer与StringBuilder都继承自AbstractStringBuilder,对于多线程的安全与非安全看到StringBuffer中方法前面的一堆synchronized就大概了解了。

我们知道使用StringBuffer等无非就是为了提高java中字符串连接的效率,因为直接使用+进行字符串连接的话,jvm会创建多个String对象,因此造成一定的开销。

AbstractStringBuilder中采用一个char数组来保存需要append的字符串,char数组有一个初始大小,当append的字符串长度超过当前char数组容量时,则对char数组进行动态扩展,也即重新申请一段更大的内存空间,然后将当前char数组拷贝到新的位置,因为重新分配内存并拷贝的开销比较大,所以每次重新申请内存空间都是采用申请大于当前需要的内存空间的方式,这里是2倍。

分类总结如下:

(1)StringBuffer在进行追加操作的时候,只是在字符串的后边进行追加操作,追加的过程也是需要进行判断预存数组大小是否超过一开始设定的16子字符,然后才可以进行追加,每次结果都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,再改变对象引用。;由于StringBuilder和StringBuffer本身都是继承自AbstractStringBuilder,在进行追加操作的时候,使用的就是AbstractStringBuilder类中的append方法,下图中展示了AbstractStringBuilder类中所有重载的append方法,根据不同的需求可以实现不同的功能,

以append(CharSequence s)为例,展示一下:(下边的方法都是AbstractStringBuilder中的方法)

@Override
    public AbstractStringBuilder append(CharSequence s) {
        if (s == null)
            return appendNull();
        if (s instanceof String)
            return this.append((String)s);
        if (s instanceof AbstractStringBuilder)
            return this.append((AbstractStringBuilder)s);

        return this.append(s, 0, s.length());
    }

首先进行判断是否为空,然后判断是哪一个实例,最后掉用append另一个重载的方法:

@Override
    public AbstractStringBuilder append(CharSequence s, int start, int end) {
        if (s == null)
            s = "null";
        if ((start < 0) || (start > end) || (end > s.length()))
            throw new IndexOutOfBoundsException(
                "start " + start + ", end " + end + ", s.length() "
                + s.length());
        int len = end - start;
        ensureCapacityInternal(count + len);
        for (int i = start, j = count; i < end; i++, j++)
            value[j] = s.charAt(i);
        count += len;
        return this;
    }

其中:

    /**
     * The count is the number of characters used.
     */
    int count;

    /**
     * The value is used for character storage.
     */
    char[] value;

可以看出:ensureCapacityInternal(count + len); 是一个扩容的过程,value[j] = s.charAt(i); 是保存追加字符串的过程,具体的过程就是再原StringBuffer对象的基础上首先通过扩容,之后在进行追加操作;

(2)StringBuffer的四个构造方法如下:

StringBuffer()
构造一个其中不带字符的字符串缓冲区,其初始容量为 16 个字符。

StringBuffer(CharSequence seq)
public java.lang.StringBuilder(CharSequence seq) 构造一个字符串缓冲区,它包含与指定的 CharSequence 相同的字符。

StringBuffer(int capacity)
构造一个不带字符,但具有指定初始容量的字符串缓冲区。

StringBuffer(String str)
构造一个字符串缓冲区,并将其内容初始化为指定的字符串内容

每个字符串缓冲区都有一定的容量。只要字符串缓冲区所包含的字符序列的长度没有超出此容量,就无需分配新的内部缓冲区数组。如果内部缓冲区溢出,则此容量自动增大。从 JDK 5 开始,为该类补充了一个单个线程使用的等价类,即 StringBuilder。与该类相比,通常应该优先使用 StringBuilder 类,因为它支持所有相同的操作,但由于它不执行同步,所以速度更快。

(3)而对于String来说,虽然追加(字符串串联)的本质也是通过创建StringBuilder对象,通过StringBuilder的append方法来实现的,但是每次追加的过程都是一次创建StringBuilder对象的过程,因也就是说在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,效率显然要比直接使用StringBuilder来说要低很多,网上也有很多测试的实验,也充分说明这一点,所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度是一定会相当慢的;

(4)而在某些特别情况下, String 对象的字符串拼接其实是被 JVM 解释成了 StringBuffer 对象的拼接,所以这些时候 String 对象的速度并不会比 StringBuffer 对象慢,而特别是以下的字符串对象生成中, String 效率是远要比 StringBuffer 快的:

String S1 = “This is only a” + “ simple” + “ test”;
StringBuffer Sb = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”);

会很惊讶的发现,生成 String S1 对象的速度简直太快了,而这个时候StringBuffer 居然速度上根本一点都不占优势。其实这是 JVM 的一个把戏,在JVM 眼里,这个String S1 = “This is only a” + “ simple” + “test”;

其实就是: String S1 = “This is only a simple test”;

所以当然不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的 String 对象的话,速度就没那么快了,譬如:

String S2 = “This is only a”;
String S3 = “ simple”;
String S4 = “ test”;
String S1 = S2 +S3 + S4;

这时候 JVM 会规规矩矩的按照原来的方式去做, S1 对象的生成速度就不像刚才那么快了,一会儿我们可以来个测试作个验证。

由此我们得到第一步结论: 在大部分情况下 StringBuffer > String

(4)总的来说,StringBuilder适合于需要进行经常性的字符追加删除操作等,String本身设计的初衷应该是为了在后边的使用过程种,不会进行太多的增删操作,毕竟String是final类型的,是不可变的对象,因此他们都有所擅长的地方,根据自己的需求进行合理的选择即可;

总结

线程安全:

StringBuffer:线程安全

StringBuilder:线程不安全

速度:

一般情况下,速度从快到慢为 StringBuilder > StringBuffer > String,当然这是相对的,不是绝对的。

使用环境:

操作少量的数据使用 String;

单线程操作大量数据使用 StringBuilder;

多线程操作大量数据使用 StringBuffer。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-13 01:31:26

JAVA的String、StringBuilder和StringBuffer类的区别的相关文章

解析Java中的String、StringBuilder、StringBuffer类(一)

引言 String 类及其相关的StringBuilder.StringBuffer 类在 Java 中的使用相当的多,在各个公司的面试中也是必不可少的.因此,在本周,我打算花费一些时间来认真的研读一下 String.StringBuilder.StringBuffer类 的相关代码. String的不可变性 这个特性是 String 相当重要的一个特性,为了深入理解,我直接贴上其源代码 public String concat(String str) { int otherLen = str.

Java中的String,StringBuilder,StringBuffer三者的区别

最近在学习Java的时候,遇到了这样一个问题,就是String,StringBuilder以及StringBuffer这三个类之间有什么区别呢,自己从网上搜索了一些资料,有所了解了之后在这里整理一下,便于大家观看,也便于加深自己学习过程中对这些知识点的记忆,如果哪里有误,恳请指正. 这三个类之间的区别主要是在两个方面,即运行速度和线程安全这两方面. 首先说运行速度,或者说是执行速度,在这方面运行速度快慢为:StringBuilder > StringBuffer > String String

[转载]Java中的String,StringBuilder,StringBuffer三者的区别

最近在学习Java的时候,遇到了这样一个问题,就是String,StringBuilder以及StringBuffer这三个类之间有什么区别呢,自己从网上搜索了一些资料,有所了解了之后在这里整理一下,便于大家观看,也便于加深自己学习过程中对这些知识点的记忆,如果哪里有误,恳请指正. 这三个类之间的区别主要是在两个方面,即运行速度和线程安全这两方面. 首先说运行速度,或者说是执行速度,在这方面运行速度快慢为:StringBuilder > StringBuffer > String String

随笔⑤ String,StringBuilder与StringBuffer类的比较

String,StringBuilder与StringBuffer类的比较 一 可变与不可变 ① String类中使用字符数组保存字符串,如下就是,因为有"final"修饰符,所以可以知道string对象是不可变的.private final char value[]; String 为不可变对象,一旦被创建,就不能修改它的值. 对于已经存在的String对象的修改都是重新创建一个新的对象,然后把新的值保存进去. String变量一旦初始化后就不能更改,禁止改变对象的状态,从而增加共享

String, StringBuilder及StringBuffer的区别

在java中,String, StringBuilder及StringBuffer经常被用来处理字符串操作. 下表列出它们的异同点: String StringBuffer StringBuilder 是否可继承 否(final) 否(final) 否(final) 是否长度可变 否 是 是 是否线程安全 ---------- 是 否 拼接效率 低 中 高 一般情况下,使用String,StringBuilder比较多.因为字符串的拼接很少会涉及到多线程,所以StringBuffer用的较少.先

String,StringBuilder,StringBuffer三者的区别

参考   String,StringBuilder,StringBuffer三者的区别 这三个类之间的区别主要是在两个方面,即运行速度和线程安全这两方面. 1.运行速度 首先说运行速度,或者说是执行速度,在这方面运行速度快慢为:StringBuilder > StringBuffer > String String最慢的原因: String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建之后该对象是不可更改的,但后两者的对象是变量,

String类和StringBuffer类的区别

首先,String和StringBuffer主要有2个区别: (1)String类对象为不可变对象,一旦你修改了String对象的值,隐性重新创建了一个新的对象,释放原String对象,StringBuffer类对象为可修改对象,每次返回的都是原对象,可以通过append()方法来修改值 (2)String类对象的性能远不如StringBuffer类. 关于以上具体解释如下: 在java中有3个类来负责字符的操作. 1.Character 是进行单个字符操作的, 2.String 对一串字符进行

String, StringBuilder 与StringBuffer的区别与联系

1.区别 (1)String构建的对象不能改变,每次对String进行操作时,如两个String相加,需要新建一个String对象,然后容纳最终的结果. 而StringBuilder与StringBuffer构建的对象可以随时在修改其内容,而无需生成新的对象.一般新建一个对象是会生成16个字节的空间,之后根据需要再增加空间. 由于一般新构建一个对象涉及分配内存空间分配.无引用对象过多时的垃圾回收等,因此,对于操作频繁的字符串需使用StringBuilder或StringBuffer. (2)St

java:String使用equals和==比较的区别

"=="操作符的作用 1.用于基本数据类型的比较 2.判断引用是否指向堆内存的同一块地址. equals所在位置: 在Object类当中,而Object是所有类的父类,包含在jdk里面,但并不适合绝大多数场景,通常需要重写 public boolean equals(Object obj) { return (this == obj); } equals的作用: 用于判断两个变量是否是对同一个对象的引用,即堆中的内容是否相同,返回值为布尔类型 equals的基本使用: boolean