美文网首页程序员Javajava大搜罗
Java中的String、StringBuffer、String

Java中的String、StringBuffer、String

作者: 逍遥ii | 来源:发表于2018-11-30 15:47 被阅读15次

    String、StringBuffer、StringBuilder有什么区别?这个问题在面试中经常碰到,今天主要讲解一下如何理解Java中的String、StringBuffer、StringBuilder

    典型回答

    String 是 Java 语言非常基础和重要的类,提供了构造和管理字符串的各种基本逻辑。它是典型的不可变类( Immutable ),被声明成为 final class,所有属性也都是 final 的。也由于它的不可变性,类似拼接、裁剪字符串等动作,都会产生新的 String 对象。由于字符串操作的普遍性,所以相关操作的效率往往对应用性能有明显影响。

    StringBuffer 是为解决上面提到拼接产生太多中间对象的问题而提供的一个类,我们可以用 append 或者 add 方法,把字符串添加到已有序列的末尾或者指定位置。StringBuffer 本质是一个线程安全的可修改字符序列,它保证了线程安全,也随之带来了额外的性能开销,所以除非有线程安全的需要,不然还是推荐使用它的后继者,也就是 StringBuilder。

    StringBuilder 是 Java 1.5 中新增的,在能力上和 StringBuffer 没有本质区别,但是它去掉了线程安全的部分,有效减小了开销,是绝大部分情况下进行字符串拼接的首选

    考点分析

    几乎所有的应用开发都离不开操作字符串,理解字符串的设计和实现以及相关工具如拼接类的使用,对写出高质量代码是非常有帮助的。关于这个问题,前面的回答是一个通常的概要性回答,至少你要知道 String 是 Immutable 的,字符串操作不当可能会产生大量临时字符串,以及线程安全方面的区别

    如果继续深入,面试官可以从各种不同的角度考察,比如可以:

    • 通过 String 和相关类,考察基本的线程安全设计与实现,各种基础编程实践。
    • 考察 JVM 对象缓存机制的理解以及如何良好地使用。
    • 考察 JVM 优化 Java 代码的一些技巧。
    • String 相关类的演进,比如 Java 9 中实现的巨大变化。
    • ...

    知识扩展

    字符串设计和实现考量

    String 是 Immutable 类的典型实现,原生的保证了基础线程安全,因为你无法对它内部数据进行任何修改,这种便利甚至体现在拷贝构造函数中,由于不可变,Immutable 对象在拷贝时不需要额外复制数据。

    我们再来看看 StringBuffer 实现的一些细节,它的线程安全是通过把各种修改数据的方法都加上 synchronized 关键字实现的,非常直白。其实,这种简单粗暴的实现方式,非常适合我们常见的线程安全类实现,不必纠结于 synchronized 性能之类的,有人说“过早优化是万恶之源”,考虑可靠性、正确性和代码可读性才是大多数应用开发最重要的因素。

    为了实现修改字符序列的目的,StringBuffer 和 StringBuilder 底层都是利用可修改的(char,JDK 9 以后是 byte)数组,二者都继承了 AbstractStringBuilder,里面包含了基本操作,区别仅在于最终的方法是否加了 synchronized

    在具体的代码书写中,应该如何选择呢?

    在没有线程安全问题的情况下,全部拼接操作是应该都用 StringBuilder 实现吗?毕竟这样书写的代码,还是要多敲很多字的,可读性也不理想,下面的对比非常明显。

    String strByBuilder  = new
    StringBuilder().append("aa").append("bb").append("cc").append
                ("dd").toString();
                 
    String strByConcat = "aa" + "bb" + "cc" + "dd";
    
    

    其实,在通常情况下,没有必要过于担心,要相信 Java 还是非常智能的。

    我们来做个实验,把下面一段代码,利用不同版本的 JDK 编译,然后再反编译,例如:

      public class StringConcat {
            public static void main(String[] args) {
                String myStr = "aa" + "bb" + "cc" + "dd";   
                 System.out.println("My String:" + myStr);   
            } 
        }
    
    

    先编译再反编译

    ${JAVA_HOME}/bin/javac StringConcat.java
    ${JAVA_HOME}/bin/javap -v StringConcat.class
    

    JDK 8 的输出片段是:

             0: ldc           #2                  // String hellojava!
             2: astore_1
             3: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
             6: new           #4                  // class java/lang/StringBuilder
             9: dup
            10: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
            13: ldc           #6                  // String Concat String:
            15: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
            18: aload_1
            19: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
            22: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
            25: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            28: return
    

    你可以看到,在 JDK 8 中,字符串拼接操作会自动被 javac 转换为 StringBuilder 操作,而在 JDK 9 里面则是因为 Java 9 为了更加统一字符串操作优化,提供了 StringConcatFactory,作为一个统一的入口。javac 自动生成的代码,虽然未必是最优化的,但普通场景也足够了,你可以酌情选择。

    参考自极客时间:Java核心技术36讲

    感谢原作者:杨晓峰老师

    相关文章

      网友评论

        本文标题:Java中的String、StringBuffer、String

        本文链接:https://www.haomeiwen.com/subject/zvuhcqtx.html