String相关的四种字符串类

本篇文章介绍String,StringBuilder,StringBuffer和StringJoner的区别和使用,以及如何进行字符串拼接的常用方法

String

String类被final修饰,不可被继承,内部由一个被final字符数组实现,因此String是一个不可变类,这意味着对String的每次修改都会创建新的存储空间,而StringBuilder,StringBuffer都是字符串变量,是可以修改的。

如果执行String a=”abc”+”def”;,实际上经历了以下过程:

  1. 创建“abc”
  2. 创建“def”
  3. 合成“abcdef”
  4. 赋值给a
    String拼接过程
    至于”+”号是如何实现拼接的,后面会讲到。

StringBuilder和StringBuffer

之所以把这两个合在一起说是因为它们基本用法都类似,它们都是字符串变量,即它们的值一般可以动态改变而不会多占据存储空间。

JDK1.8中对StringBuilder和StringBuffer定义为AbstractStringBuilder的子类,这也说明了两者有巨大的相似性,AbstractStringBuilder定义为如下结构(StringBuilder始于JDK1.5):
StringBuilder内部结构
字符串数组value不被final修饰,证明value可变;由于存在count表示实际字符个数,这意味着value中的数据不是充满了整个value空间。

StringBuilder和StringBuffer的区别在于

1. StringBuilder是非线程安全的,StringBuffer是线程安全的
2. StringBuilder速度快,StringBuffer速度慢

StringBuilder和StringBuffer常用方法

    package test.string;
    public class StringTest {
        public static void main(String[] args) {
            StringBuilder builder=new StringBuilder();
            /*append()方法*/
            builder.append("hi ").append("my name is ").append("smartpig");
            System.out.println(builder.toString());
            /*insert()方法*/
            builder.insert(2, ",");
            System.out.println(builder.toString());
            /*replace()方法*/
            builder.replace(0, 2, "hello");
            System.out.println(builder.toString());
            /*reverse()方法*/
            builder.reverse();
            System.out.println(builder.toString());
            /*其他方法都和String类似*/
        }
    }

结果如下:
result
append()方法功能和“+“一样,用来拼接字符串,具体实现下面会讲到。

StringBuffer和StringBuilder方法一样,唯一区别在于StringBuffer是线程安全的。

字符串拼接

方法1 “+“语法糖

使用最多的“+“其实是一种Java的语法糖,语法糖被定义为:对语言的功能没有影响但能方便程序员使用的一种语法。

“+“的内部实现其实最终依赖于StringBuilder,还是上面的例子String a=”abc”+”def”;
底层真正的实现是:String a=(new StringBuilder).append(“abc”).append(“def”).toString();

内部使用了StringBuilder,而且每次都会新建一个StringBuilder对象,大量操作必然导致资源浪费,所以在阿里巴巴开发手册中就明确指出:

循环体内,字符串的连接方式,使用StringBuilder的append()方法进行扩展

方法2 concat()方法

String.concat()也是字符串拼接的一种方法,但没有“+“使用广泛

concat()内部又是如何实现?查阅JDK源码:

    public String concat(String str) {
        int otherLen = str.length();
        if (otherLen == 0) {
            return this
        }
        int len = value.length;
        char buf[] = Arrays.copyOf(value, len + otherLen);
        str.getChars(buf, len);
        return new String(buf, true);
    }

主要思想是创建一个新的字符数组,长度为当前字符串和待拼接字符串之后,然后将两个字符串复制到新的字符数组中,并用新的字符数组new一个新的String,这也印证了String是不可变的字符串。

方法3 append()方法

StringBuilder和StringBuffer的append()方法基本类似,以StringBuilder为例:

    public StringBuilder append(String str) {
            super.append(str);
            return this;
    }

内部调用父类的append()方法,AbstractStringBuilder的append()方法定义如下:

    public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }

append()方法直接将待拼接字符串拷贝到内部的value数组中,然后修改count的值;当然在此之前,首先要保证value的容量足够,如果不够,会先进行扩容,再拷贝字符串。

方法4 使用StringJoiner

StringJoiner是一个由于构造由分隔符和前后缀组成的字符串。
与其他字符串不同的是,StringJoiner的构造方法只有两个,不存在空参构造函数:

  1. 第一个构造函数有一个参数,表示指定的分隔符
  2. 第二个构造函数有三个参数,表示指定的分隔符和指定的前缀和后缀

具体使用如下:

    package test.string;
    import java.util.StringJoiner;
    public class StringJoinerTest {
        public static void main(String[] args) {
            //第一种构造函数
            StringJoiner joiner1=new StringJoiner("/");
            joiner1.add("2019").add("04").add("11");
            System.out.println(joiner1.toString());
            //第二种构造函数
            StringJoiner joiner2=new StringJoiner(",","[","]");
            joiner2.add("Java").add("Python").add("Kotlin");
            System.out.println(joiner2.toString());
        }
        /*结果如下:
        * 	2019/04/11
        *  [Java,Python,Kotlin]
        */
    }

上面的例子非常清楚的展示了如何使用StringJonier的构造函数,以及如何使用add()方法进行字符串拼接。StringJoiner的拼接字符串非常灵活也很有用处。

StringJoiner的add()方法内部的实现也是基于StringBuilder.append()方法

    public StringJoiner add(CharSequence newElement) {
        prepareBuilder().append(newElement);
  	    return this;
    }

其中的prepareBuilder()返回的是一个StringBuilder对象,再调用prepareBuilder()后,完全是基于StringBuilder来实现的

    private StringBuilder prepareBuilder() {
        if (value != null) {
            value.append(delimiter);
        } else {
            value = new StringBuilder().append(prefix);
        }
        return value;
    }

但是并没有看到对后缀的处理,其实后缀的处理放在了toString()方法中。

StringJoiner使用StringBuilder,效率和StringBuilder不相上下,是非线程安全的。

总结

  1. 简单字符串拼接使用“+“
  2. 循环体内字符串拼接使用StringBuilder/StringBuffer
  3. 特殊格式字符串拼接使用StringJoiner
  4. 多线程下使用StringBuffer

欢迎访问我的个人博客网站smartpig612.club和微信公众号SmartPig获取更多干货!
感谢您的支持!
扫码关注SmartPig微信公众号:
wechat

猜你喜欢

转载自blog.csdn.net/tianc_pig/article/details/89226339