java stringbuffer原理_深入理解String, StringBuffer, StringBuilder的区别(基于JDK1.8)

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

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

一、String

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

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

public final classStringimplements java.io.Serializable, Comparable, CharSequence {private final charvalue[];private int hash; //Default to 0

publicString() {this.value = new char[0];

}publicString(String original) {this.value =original.value;this.hash =original.hash;

}public String(charvalue[]) {this.value =Arrays.copyOf(value, value.length);

}

...

}

View Code

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

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

public classStringTest {public static voidmain(String[] args) {

String str1= "abc";

String str2= "abc";

String Str3= new String("abc");

}

}

Vew Code

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

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

Classfile /C:/Users/jiang/workspace/test/src/test/StringTest.classLast modified2018-7-8; size 363bytes

MD5 checksum f7e4243b0247fb20c5a336d4ba0a580f

Compiled from"StringTest.java"

public classtest.StringTest

minor version:0major version:52flags: ACC_PUBLIC, ACC_SUPER

Constant 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

{publictest.StringTest();

descriptor: ()V

flags: ACC_PUBLIC

Code:

stack=1, locals=1, args_size=1

0: aload_01: invokespecial #1 //Method java/lang/Object."":()V

4: returnLineNumberTable:

line3: 0

public static voidmain(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_13: ldc #2 //String abc

5: astore_26: new #3 //class java/lang/String

9: dup10: ldc #2 //String abc

12: invokespecial #4 //Method java/lang/String."":(Ljava/lang/String;)V

15: astore_316: returnLineNumberTable:

line6: 0line7: 3line8: 6line9: 16}

SourceFile:"StringTest.java"

View Code

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

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

public classStringTest {public static voidmain(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文件:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

Classfile /C:/Users/jiang/workspace/test/src/test/StringTest.classLast modified2018-7-8; size 493bytes

MD5 checksum c02bd18ed3ecbe46f9859bf5e272c663

Compiled from"StringTest.java"

public classtest.StringTest

minor version:0major version:52flags: ACC_PUBLIC, ACC_SUPER

Constant 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;

{publictest.StringTest();

descriptor: ()V

flags: ACC_PUBLIC

Code:

stack=1, locals=1, args_size=1

0: aload_01: invokespecial #1 //Method java/lang/Object."":()V

4: returnLineNumberTable:

line3: 0

public static voidmain(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_13: new #3 //class java/lang/StringBuilder

6: dup7: invokespecial #4 //Method java/lang/StringBuilder."":()V

10: aload_111: 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_223: new #3 //class java/lang/StringBuilder

26: dup27: invokespecial #4 //Method java/lang/StringBuilder."":()V

30: aload_231: 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_343: returnLineNumberTable:

line6: 0line9: 3line10: 23line11: 43}

SourceFile:"StringTest.java"

View Code

二、StringBuilder

也是先来看StringBuilder的源码:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

public final classStringBuilderextendsAbstractStringBuilderimplementsjava.io.Serializable, CharSequence

{publicStringBuilder() {super(16);

}publicStringBuilder(String str) {super(str.length() + 16);

append(str);

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

}

...

}abstract class AbstractStringBuilder implementsAppendable, CharSequence {char[] value;intcount;

AbstractStringBuilder(intcapacity) {

value= new char[capacity];

}publicAbstractStringBuilder append(String str) {if (str == null)returnappendNull();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的源码如下:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

public final classStringBufferextendsAbstractStringBuilderimplementsjava.io.Serializable, CharSequence

{private transient char[] toStringCache;publicStringBuffer() {super(16);

}publicStringBuffer(String str) {super(str.length() + 16);

append(str);

}public synchronizedStringBuffer 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。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值