String和StringBuffer和StringBuilder

158 阅读5分钟

Java的String和StringBuffer和StringBuilder

String 由 char[] 数组构成,使用了 final 修饰,是不可变对象,可以理解为常量,线程安全;对 String 进行改变时每次都会新生成一个 String 对象,然后把指针指向新的引用对象。 StringBuffer 线程安全;StringBuiler 线程不安全。 操作少量字符数据用 String;单线程操作大量数据用 StringBuilder;多线程操作大量数据用 StringBuffer。

String常用方法

package com.daylywork.study;
​
import lombok.extern.slf4j.Slf4j;
​
import java.util.ArrayList;
import java.util.List;
​
@Slf4j
public class MyString {
    public static void main(String[] args){
        String ss="abCDefgdeaaakkkdea";
        String sss ="  This is a hello ";
        getNewString(ss);
        log.info("ss.length的结果是int类型的,而log.info需要的是String类型的,所以通过String.valueOf将int修改为String:"+String.valueOf(ss.length()));
        System.out.println("直接打印字符串的长度:"+ss.length());
        System.out.println("打印这个元素在字符串中第一次出现的位置,如果没出现过返回-1:"+ss.indexOf("dea"));
        System.out.println("打印这个元素在字符串中最后一次出现的位置,如果没出现过返回-1:"+ss.lastIndexOf("ea"));
        System.out.println("将小写字母修改为大写字母:"+ss.toUpperCase());
        System.out.println("将大写字母修改为小写字母:"+ss.toLowerCase());
        System.out.println(sss);
        System.out.println("将字符串的前后空格删除掉:"+sss.trim());
        System.out.println("截取start到end的字符串,注意这两个是左闭右开:"+ss.substring(2,5));
        String a="zhangsan";
        String aa="gs";
        System.out.println("判断字符串aa在不在a里:"+a.contains(aa));
        System.out.println("将字符串a中的gs替换成lisi,并不会修改字符串本身"+a.replace("s","lisi"));
        System.out.println("将字符串a中所有的a替换成ccc,看起来是不是和replace一毛一样:"+a.replaceAll("a","ccc"));
        System.out.println("不一样的是replacxeAll是支持正则的:"+a.replaceAll("\w","*"));
        System.out.println("将字符串a中的第一个a替换成ccc,并不会修改字符串本身:"+a.replaceFirst("a","ccc"));
        String b="北京/朝阳/常营";
        String bbb ="北京*朝阳*常营";
        System.out.println("split,意思为分割,根据某一值来分割字符串,并成为一个数组:"+b.split("/")[0]+b.split("/")[1]+b.split("/")[2]);
        System.out.println("split,意思为分割,根据某一值来分割字符串,并成为一个数组:"+bbb.split("\*")[0]+bbb.split("\*")[1]+bbb.split("\*")[2]);
        System.out.println(String.join(aa,a));
        List l=new ArrayList<String>();
        l.add("常营");
        l.add("朝阳");
        l.add("北京");
        System.out.println("和toCharArray相反,这次是把数字变成字符串啦:"+String.join("+",l));
        String strone = String.format("hello,%s,%s和%s,你们%d个好啊","张三","lisi","王武",3);
        System.out.println("format字符串格式化:"+strone);
    }
        
    private static void getNewString(String s) {
        /**
         * 将字符串修改为数组
         * */
        char[] c = s.toCharArray();
        for (int i=c.length-1;i>=0;i--){
            System.out.print(c[i]);
        }
        System.out.println("");
    }
}

结果:

aedkkkaaaedgfeDCba
19:47:12.267 [main] INFO com.daylywork.study.MyString - ss.length的结果是int类型的,而log.info需要的是String类型的,所以通过String.valueOfint修改为String18
直接打印字符串的长度:18
打印这个元素在字符串中第一次出现的位置,如果没出现过返回-17
打印这个元素在字符串中最后一次出现的位置,如果没出现过返回-116
将小写字母修改为大写字母:ABCDEFGDEAAAKKKDEA
将大写字母修改为小写字母:abcdefgdeaaakkkdea
  This is a hello 
将字符串的前后空格删除掉:This is a hello
截取startend的字符串,注意这两个是左闭右开:CDe
判断字符串aa在不在a里:true
将字符串a中的gs替换成lisi,并不会修改字符串本身zhanglisian
将字符串a中所有的a替换成ccc,看起来是不是和replace一毛一样:zhcccngscccn
不一样的是replacxeAll是支持正则的:********
将字符串a中的第一个a替换成ccc,并不会修改字符串本身:zhcccngsan
split,意思为分割,根据某一值来分割字符串,并成为一个数组:北京朝阳常营
split,意思为分割,根据某一值来分割字符串,并成为一个数组:北京朝阳常营
zhangsantoCharArray相反,这次是把数字变成字符串啦:常营+朝阳+北京
format字符串格式化:hello,张三,lisi和王武,你们3个好啊

StringBuilder和StringBuffer

StringBuffer

我们来看一下StringBuffer的部分源码

发现方法上都是带synchronized,我们知道这个词是用来确保线程安全的,所以StringBuffer是线程安全的

    @Override
    public synchronized int length() {
        return count;
    }
​
    @Override
    public synchronized int capacity() {
        return value.length;
    }
​
​
    @Override
    public synchronized void ensureCapacity(int minimumCapacity) {
        super.ensureCapacity(minimumCapacity);
    }
​
    /**
     * @since      1.5
     */
    @Override
    public synchronized void trimToSize() {
        super.trimToSize();
    }
​
    /**
     * @throws IndexOutOfBoundsException {@inheritDoc}
     * @see        #length()
     */
    @Override
    public synchronized void setLength(int newLength) {
        toStringCache = null;
        super.setLength(newLength);
    }

StringBuilder

我们来看看StringBuilder的部分源码

发现方法上没有synchronized,我们知道这个词是用来确保线程安全的,所以StringBuffer是线程不安全的

    @Override
    public StringBuilder insert(int offset, float f) {
        super.insert(offset, f);
        return this;
    }
​
    /**
     * @throws StringIndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public StringBuilder insert(int offset, double d) {
        super.insert(offset, d);
        return this;
    }
​
    @Override
    public int indexOf(String str) {
        return super.indexOf(str);
    }
​
    @Override
    public int indexOf(String str, int fromIndex) {
        return super.indexOf(str, fromIndex);
    }

StringBuffer和StringBuilder和String速度的对比

String最慢的原因:String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建之后该对象是不可更改的,但后两者的对象是变量,是可以更改的.

package com.daylywork.study;
​
public class MyBuilderAndBuffer {
    public static void main(String[] args){
        String str="abc";
        str=str+"de";
        System.out.println(str);
        /**
         * 输出结果:abcde
         * 看起来像是a这个字符串有了变化,其实,这只是一种假象罢了,JVM对于这几行代码是这样处理的,首先创建一个String对象str,并把“abc”赋值给str,
         * 然后在a=a+"lisi"中,其实JVM又创建了一个新的对象也名为str,然后再把原来的str的值和“de”加起来再赋值给新的str,
         * 而原来的str就会被JVM的垃圾回收机制(GC)给回收掉了,所以,str实际上并没有被更改,也就是前面说的String对象一旦创建之后就不可更改了。
         * 所以,Java中对String对象进行的操作实际上是一个不断创建新的对象并且将旧的对象回收的一个过程,所以执行速度很慢。
         * */
        StringBuffer stringBuffer=new StringBuffer("qazwsx");
        System.out.println("stringBuffer的逆序输出:"+stringBuffer.reverse());
        StringBuilder stringBuilder=new StringBuilder("edcrfv");
        System.out.println("stringBuilder的逆序输出:"+stringBuilder.reverse());
    }
}

结果

abcde
stringBuffer的逆序输出:xswzaq
stringBuilder的逆序输出:vfrcde

insert和append的区别

package com.daylywork.study;
​
public class MyBuilderAndBuffer {
    public static void main(String[] args){
        StringBuffer stringBuffer=new StringBuffer("qazwsx");
        System.out.println("append只能在末尾:"+stringBuffer.append("zhangsan"));
        System.out.println("insert能在字符串的任意位置添加:"+stringBuffer.insert(0,"zhangsan"));
    }
}

结果:

append只能在末尾:qazwsxzhangsan
insert能在字符串的任意位置添加:zhangsanqazwsxzhangsan