JAVA中的String类、StringBuffer类、StringBuilder类

小伙子!打起精神来,让自己活的贵一点!

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

  • String类对字符串的保存格式:
private final char value[];

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

String的相关知识

  • StringBuilder、StringBuffer两个类对字符串的保存格式:
char[] value;

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

  • StringBuilder、StringBuffer与String类之间的转换
public abstract String toString();

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

StringBuilder a  = new StringBuilder("hello");

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

线程安全相关的问题

  • String类

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

  • StringBuffer类与StringBuilder类
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

java学习资料分享:关注公众号[Swen学java]即可免费领取详情见java学习资源汇总

java学习资源框架.png

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