博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
深入理解String, StringBuffer, StringBuilder的区别(基于JDK1.8)
阅读量:6292 次
发布时间:2019-06-22

本文共 9132 字,大约阅读时间需要 30 分钟。

        String、StringBuffer、StringBuilder都是JAVA中常用的字符串操作类,对于他们的区别大家也都能耳熟能详,但底层到底是怎样实现的呢?今天就再深入分析下这三种字符串操作的区别、各自的原理及使用场景。

       请尊重作者劳动成果,转载请标明原文链接:

       

一、String

       先来看一下JDK中String中的部分源码:

public final class String    implements java.io.Serializable, Comparable
, CharSequence { private final char value[]; private int hash; // Default to 0 public String() { this.value = new char[0]; } public String(String original) { this.value = original.value; this.hash = original.hash; } public String(char value[]) { this.value = Arrays.copyOf(value, value.length); } ...}
View Code

        可以看到String类、以及value都是final类型的,这样就表明String是无法被继承的,value是无法被改写的。当通过String的构造函数初始化新的String对象时,也只是根据传入的引用对象的value和hashcode进行了赋值。看下面的例子:

public class StringTest {    public static void main(String[] args) {        String str1 = "abc";        String str2 = "abc";        String Str3 = new String("abc");    }}
Vew Code

       执行javac StringTest.java后,通过javap -v StringTest.class看下生成的class文件:

Classfile /C:/Users/jiang/workspace/test/src/test/StringTest.class  Last modified 2018-7-8; size 363 bytes  MD5 checksum f7e4243b0247fb20c5a336d4ba0a580f  Compiled from "StringTest.java"public class test.StringTest  minor version: 0  major version: 52  flags: ACC_PUBLIC, ACC_SUPERConstant pool:   #1 = Methodref          #6.#15         // java/lang/Object."
":()V #2 = String #16 // abc #3 = Class #17 // java/lang/String #4 = Methodref #3.#18 // java/lang/String."
":(Ljava/lang/String;)V #5 = Class #19 // test/StringTest #6 = Class #20 // java/lang/Object #7 = Utf8
#8 = Utf8 ()V #9 = Utf8 Code #10 = Utf8 LineNumberTable #11 = Utf8 main #12 = Utf8 ([Ljava/lang/String;)V #13 = Utf8 SourceFile #14 = Utf8 StringTest.java #15 = NameAndType #7:#8 // "
":()V #16 = Utf8 abc #17 = Utf8 java/lang/String #18 = NameAndType #7:#21 // "
":(Ljava/lang/String;)V #19 = Utf8 test/StringTest #20 = Utf8 java/lang/Object #21 = Utf8 (Ljava/lang/String;)V{ public test.StringTest(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."
":()V 4: return LineNumberTable: line 3: 0 public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=4, args_size=1 0: ldc #2 // String abc 2: astore_1 3: ldc #2 // String abc 5: astore_2 6: new #3 // class java/lang/String 9: dup 10: ldc #2 // String abc 12: invokespecial #4 // Method java/lang/String."
":(Ljava/lang/String;)V 15: astore_3 16: return LineNumberTable: line 6: 0 line 7: 3 line 8: 6 line 9: 16}SourceFile: "StringTest.java"
View Code

        可以看到对于相同的字符串“abc”的引用都是相同的(对于常量池中的相同位置),这样能够节省内存空间,但是缺点就是对于频繁的字符串拼接操作,会造成内存空间的浪费。(需要注意的是这种字符串的拼接操作,从JDK8 开始,会自动被编译成StringBuilder,是不是很666^_^,但还是建议不通过JDK途径去自动转。)看下面的代码:

public class StringTest {    public static void main(String[] args) {        String str1 = "abc";        //String str2 = "abc";        //String str3 = new String("abc");        String str4 = str1 + "d";        String str5 = str4 + "e";    }}
View Code

       然后再通过javap看下class文件:

Classfile /C:/Users/jiang/workspace/test/src/test/StringTest.class  Last modified 2018-7-8; size 493 bytes  MD5 checksum c02bd18ed3ecbe46f9859bf5e272c663  Compiled from "StringTest.java"public class test.StringTest  minor version: 0  major version: 52  flags: ACC_PUBLIC, ACC_SUPERConstant pool:   #1 = Methodref          #10.#19        // java/lang/Object."
":()V #2 = String #20 // abc #3 = Class #21 // java/lang/StringBuilder #4 = Methodref #3.#19 // java/lang/StringBuilder."
":()V #5 = Methodref #3.#22 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #6 = String #23 // d #7 = Methodref #3.#24 // java/lang/StringBuilder.toString:()Ljava/lang/String; #8 = String #25 // e #9 = Class #26 // test/StringTest #10 = Class #27 // java/lang/Object #11 = Utf8
#12 = Utf8 ()V #13 = Utf8 Code #14 = Utf8 LineNumberTable #15 = Utf8 main #16 = Utf8 ([Ljava/lang/String;)V #17 = Utf8 SourceFile #18 = Utf8 StringTest.java #19 = NameAndType #11:#12 // "
":()V #20 = Utf8 abc #21 = Utf8 java/lang/StringBuilder #22 = NameAndType #28:#29 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #23 = Utf8 d #24 = NameAndType #30:#31 // toString:()Ljava/lang/String; #25 = Utf8 e #26 = Utf8 test/StringTest #27 = Utf8 java/lang/Object #28 = Utf8 append #29 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder; #30 = Utf8 toString #31 = Utf8 ()Ljava/lang/String;{ public test.StringTest(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."
":()V 4: return LineNumberTable: line 3: 0 public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=4, args_size=1 0: ldc #2 // String abc 2: astore_1 3: new #3 // class java/lang/StringBuilder 6: dup 7: invokespecial #4 // Method java/lang/StringBuilder."
":()V 10: aload_1 11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 14: ldc #6 // String d 16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 22: astore_2 23: new #3 // class java/lang/StringBuilder 26: dup 27: invokespecial #4 // Method java/lang/StringBuilder."
":()V 30: aload_2 31: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 34: ldc #8 // String e 36: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 39: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 42: astore_3 43: return LineNumberTable: line 6: 0 line 9: 3 line 10: 23 line 11: 43}SourceFile: "StringTest.java"
View Code

二、StringBuilder

       也是先来看StringBuilder的源码:

public final class StringBuilder    extends AbstractStringBuilder    implements java.io.Serializable, CharSequence{        public StringBuilder() {        super(16);    }        public StringBuilder(String str) {        super(str.length() + 16);        append(str);    }        public StringBuilder append(String str) {        super.append(str);        return this;    }        ...}abstract class AbstractStringBuilder implements Appendable, CharSequence {        char[] value;        int count;        AbstractStringBuilder(int capacity) {        value = new char[capacity];    }        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;    }    ...}
View Code

        可以看到StringBuilder的value是个char数组,(当然从JDK9开始,value从char数组变成了byte数组)。每次append时都是通过调用native的System.arraycopy实现的(在getChars中调用的)。

三、StringBuffer

S       tringBuffer的源码如下:

public final class StringBuffer    extends AbstractStringBuilder    implements java.io.Serializable, CharSequence{    private transient char[] toStringCache;    public StringBuffer() {        super(16);    }    public StringBuffer(String str) {        super(str.length() + 16);        append(str);    }    public synchronized StringBuffer append(String str) {        toStringCache = null;        super.append(str);        return this;    }    ...}
View Code

        和StringBuilder一样,都是用了char数组保存value,append也是调用了AbstractStringBuilder的append方法。区别只是在于char数组加了transient关键字,以及方法上加了synchronized方法。

       综上所述,String、StringBuilder、StringBuffer的使用场景如下:

       当处理定长字符串时,建议用String;

       当处理变长字符串时,并且是单线程环境时,建议用StringBuilder;

       当处理变长字符串时,并且是多线程环境时,建议用StringBuffer。

转载于:https://www.cnblogs.com/jpcflyer/p/9280501.html

你可能感兴趣的文章
Linux 性能诊断 perf使用指南
查看>>
实操分享:看看小白我如何第一次搭建阿里云windows服务器(Tomcat+Mysql)
查看>>
Sphinx 配置文件说明
查看>>
数据结构实践——顺序表应用
查看>>
python2.7 之centos7 安装 pip, Scrapy
查看>>
机智云开源框架初始化顺序
查看>>
Spark修炼之道(进阶篇)——Spark入门到精通:第五节 Spark编程模型(二)
查看>>
一线架构师实践指南:云时代下双活零切换的七大关键点
查看>>
ART世界探险(19) - 优化编译器的编译流程
查看>>
玩转Edas应用部署
查看>>
music-音符与常用记号
查看>>
sql操作命令
查看>>
zip 数据压缩
查看>>
Python爬虫学习系列教程
查看>>
【数据库优化专题】MySQL视图优化(二)
查看>>
【转载】每个程序员都应该学习使用Python或Ruby
查看>>
PHP高级编程之守护进程,实现优雅重启
查看>>
PHP字符编码转换类3
查看>>
rsync同步服务配置手记
查看>>
http缓存知识
查看>>