程序员

JAVA中的String类、StringBuffer类、Stri

2018-11-23  本文已影响33人  Swen_9826
小伙子!打起精神来,让自己活的贵一点!

String类、StringBuffer类、StringBuilder类三者之间的关系

private final char value[];

从源码中可以看出来,它是被final修饰的,是不可变的,从它被创建直至被销毁,它的字符序列都没有改变,我们对它的一系列操作都是通过创建新的String对象来完成的。

String的相关知识

char[] value;

StringBuilder、StringBuffer两个类都继承自抽象类AbstractStringBuilder,它们是可变的。

public abstract String toString();

当我们通过StringBuilder、StringBuffer对象进行一系列操作后得到了我们最终想要的字符串之后,可以通过toString()方法来将StringBuilder、StringBuffer对象转化为String对象。

StringBuilder a  = new StringBuilder("hello");

同时也可以将String类型转换为StringBuilder或者StringBuffer类型。

线程安全相关的问题

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

public synchronized StringBuffer append(Object obj) {
    super.append(String.valueOf(obj));
    return this;
}
public synchronized StringBuffer reverse() {
    super.reverse();
    return this;
}

从代码可以看出来StringBuffer类是被synchronized修饰的,所以是线程安全的,StringBuffer和StringBuilder类功能基本相似,最大的区别在与StringBuilder是没有被synchronized修饰的,因此线程不安全,但是StringBuilder类的速度要比String类与StringBuffer要快一点。

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

StringBuilder类不是线程安全的。由于不同步,StringBuilder的性能可以比StringBuffer更好。因此,如果在单线程环境中工作,使用StringBuilder,而不是StringBuffer可能会有更高的性能。

String 字符串常量
StringBuffer 字符串变量(线程安全)
StringBuilder 字符串变量(非线程安全)
在大部分情况下速度: StringBuilder > StringBuffer
在大部分情况下速度: StringBuffer > String

速度测试代码

public class Test {
    
    final static int time = 50000; //循环次数 
    
    public static void main(String[] args) {
        Test test = new Test();
        
        String s1 =  "String类测试:";
        StringBuffer st1 = new StringBuffer( "StringBuffer类测试:");
        StringBuilder st2 = new StringBuilder( "StringBuilder类测试:");
                    
        test.test(s1);      
        test.test(st1);     
        test.test(st2);
        test.test2();
        test.test3();
    }       
    /*
     * String类测试方法
     */
    public void test(String s){
        long begin = System.currentTimeMillis();//获取当前系统时间(毫秒数),开始
        for(int i=0; i<time; i++){
            s += "hello";
        }
        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("hello");
        }
        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("hello");
        }
        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)+"毫秒");
    } 
}
//测试结果:
操作java.lang.String类型使用的时间为:9954毫秒
操作java.lang.StringBuffer类型使用的时间为:10毫秒
操作java.lang.StringBuilder类型使用的时间为:0毫秒
操作字符串对象引用相加类型使用的时间为:15毫秒
操作字符串相加使用的时间为:0毫秒

结果可以看出,在不必考虑到线程同步问题,我们应该优先使用StringBuilder类;如果要保证线程安全,自然是StringBuffer;能直接操作字符串不用字符串引用就直接操作字符串

StringBuilder类与StringBuffer类的相同点

方法 说明
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); 将字符串的子字符串复制给数组

StringBuilder类与StringBuffer类常用方法测试代码

public class Test { 
    
    public static void main(String[] args) {
        Test test = new Test();
        StringBuffer strbf = new StringBuffer("Hello Swen");    
        StringBuilder strbl = new StringBuilder(30);
        
        System.out.println("testStringBuffer如下:");
        test.testStringBuffer(strbf);
        System.out.println("testStringBuilder如下:");
        test.testStringBuilder(strbl);
    }       
    /*
     * StringBuffer类测试方法
     */
    public void testStringBuffer(StringBuffer str){
        //增加字符串内容的方法
        //append(参数),追加内容到当前对象的末尾
        str.append(" 你要加油学习!");
        System.out.println("追加内容到当前对象的末尾:"+str);        
        // insert(位置, 参数),在对象中插入内容
        str.insert(10,',');
        System.out.println("在对象中插入内容:"+str);
      
        //操作字符串内容的方法
        //delete(int start, int end),删除指定区域的字符串
        str.delete(10, 18);
        System.out.println("删除指定区域的字符串:"+str);
        //deleteCharAt(int index),删除指定位置的字符
        str.deleteCharAt(10);
        System.out.println("删除指定位置的字符:"+str);
        //setCharAt(int index, char newChar),修改对象中索引值为index位置的字符为新的字符ch
        str.setCharAt(5, '-');
        System.out.println("修改对象中索引值为index位置的字符为新的字符ch:"+str);
        //replace(int start, int end, String s), 用新的字符串替换指定区域的字符串
        str.replace(6, 10, "world");
        System.out.println("用新的字符串替换指定区域的字符串:"+str);
        // reverse()内容反转
        str.reverse();
        System.out.println("内容反转:"+str);
        //将字符串的子字符串复制给数组。
        char[] ch  = new char[5];
        str.getChars(0, 3, ch, 0);
        System.out.println("将字符串的子字符串复制给数组:"+Arrays.toString(ch));
    }
    /*
     * StringBuilder类测试方法
     */
    public void testStringBuilder(StringBuilder str){
        
        str.append("Hello Swen");
        System.out.println("追加内容到当前对象的末尾:"+str);
        //length(),获取字符串长度
        System.out.println("字符串长度为:"+str.length());
        //capacity(),获取字符串的容量
        System.out.println("字符串容量为:"+str.capacity());
        
        //有关字符串空间的方法
        //setLength(int newSize),设置字符串缓冲区大小
        str.setLength(20);
        System.out.println("setLength后,字符串长度为:"+str.length());
        System.out.println("setLength后,字符串容量为:"+str.capacity());

        //trimToSize(),存储空间缩小到和字符串长度一样的长度
        str.trimToSize();
        System.out.println("trimToSize后,字符串长度为:"+str.length());
        System.out.println("trimToSize后,字符串容量为:"+str.capacity());
    }
    
}

StringBuilder类与StringBuffer类常用方法测试结果

testStringBuffer如下:
追加内容到当前对象的末尾:Hello Swen 你要加油学习!
在对象中插入内容:Hello Swen, 你要加油学习!
删除指定区域的字符串:Hello Swen!
删除指定位置的字符:Hello Swen
修改对象中索引值为index位置的字符为新的字符ch:Hello-Swen
用新的字符串替换指定区域的字符串:Hello-world
内容反转:dlrow-olleH
将字符串的子字符串复制给数组:[d, l, r,  ,  ]

testStringBuilder如下:
追加内容到当前对象的末尾:Hello Swen
字符串长度为:10
字符串容量为:30
setLength后,字符串长度为:20
setLength后,字符串容量为:30
trimToSize后,字符串长度为:20
trimToSize后,字符串容量为:20
上一篇下一篇

猜你喜欢

热点阅读