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

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

https://www.cnblogs.com/jpcflyer/p/9280501.html

一、String

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

public final class String
implements java.io.Serializable, Comparable<String>, 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);
} ...
}

可以看到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_SUPER
Constant pool:
#1 = Methodref #6.#15 // java/lang/Object."<init>":()V
#2 = String #16 // abc
#3 = Class #17 // java/lang/String
#4 = Methodref #3.#18 // java/lang/String."<init>":(Ljava/lang/String;)V
#5 = Class #19 // test/StringTest
#6 = Class #20 // java/lang/Object
#7 = Utf8 <init>
#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 // "<init>":()V
#16 = Utf8 abc
#17 = Utf8 java/lang/String
#18 = NameAndType #7:#21 // "<init>":(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."<init>":()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."<init>":(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"

可以看到对于相同的字符串“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";
}
}

然后再通过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_SUPER
Constant pool:
#1 = Methodref #10.#19 // java/lang/Object."<init>":()V
#2 = String #20 // abc
#3 = Class #21 // java/lang/StringBuilder
#4 = Methodref #3.#19 // java/lang/StringBuilder."<init>":()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 <init>
#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 // "<init>":()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."<init>":()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."<init>":()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."<init>":()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"

二、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;
} ...
}

可以看到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;
}
...
}

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

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

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

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

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

深入理解String, StringBuffer, StringBuilder的区别(基于JDK1.8)的更多相关文章

  1. String,StringBuffer,StringBuilder的区别及其源码分析

    String,StringBuffer,StringBuilder的区别这个问题几乎是面试必问的题,这里做了一些总结: 1.先来分析一下这三个类之间的关系 乍一看它们都是用于处理字符串的java类,而 ...

  2. String,StringBuffer,StringBuilder的区别

    public static void main(String[] args) { String str = new String("hello...."); StringBuffe ...

  3. [置顶] String StringBuffer StringBuilder的区别剖析

    这是一道很常见的面试题目,至少我遇到过String/StringBuffer/StringBuilder的区别:String是不可变的对象(final)类型,每一次对String对象的更改均是生成一个 ...

  4. Question 20171115 String&&StringBuffer&&StringBuilder的区别与联系?

    Question 20171114 String&&StringBuffer&&StringBuilder的区别和联系 创建成功的String对象,其长度是固定的,内容 ...

  5. java中 String StringBuffer StringBuilder的区别

    * String类是不可变类,只要对String进行修改,都会导致新的对象生成. * StringBuffer和StringBuilder都是可变类,任何对字符串的改变都不会产生新的对象. 在实际使用 ...

  6. Android/Java 中的 String, StringBuffer, StringBuilder的区别和使用

    Android 中的 String, StringBuffer 和 StringBuilder 是移动手机开发中经常使用到的字符串类.做为基础知识是必须要理解的,这里做一些总结. A.区别 可以从以下 ...

  7. 在JAVA中,String,Stringbuffer,StringBuilder 的区别

    首先是,String,StringBuffer的区别 两者的主要却别有两方面,第一是线程安全方面,第二是效率方面 线程安全方面: String  不是线程安全的,这意味着在不同线程共享一个String ...

  8. String,StringBuffer,StringBuilder三者区别

    String:每次改变,String都会重新构造,内存指针都会改变 StringBuffer:主要用在全局变量中 StringBuilder:在线程内完成字符拼接,因为线程是不安全的,所以完成后可以丢 ...

  9. 从源码看String,StringBuffer,StringBuilder的区别

    前言 看了一篇文章,大概是讲面试中的java基础的,有如题这么个面试题.我又翻了一些文章看了下,然后去看源码.看一下源码大概能更加了解一些. String String类是final的,表示不可被继承 ...

随机推荐

  1. URL重写中的中文参数问题

    在做搜索功能时,需要输入关键字,如果搜索出来的结果很多,又需要分页.这里用URL重写技术(即href="?keyword=关键字&page=分页数"),就涉及到了传递中文关 ...

  2. No module named HTMLTestRunner

    今天把我在公司写的自动化项目copy回家,在家里的电脑运行是报了个No module named HTMLTestRunner,看到后心想这问题好解决嘛,一个pip install HTMLTestR ...

  3. c#Dapper 批量插入Mysql

    <connectionStrings> <add name="sqlconnectionString" connectionString="server ...

  4. js 库

    plupload.full.min.js 前端上传利器 jQuery WeUI - V1.0.1 微信公众号开发利器

  5. JS中的offset scroll event client

    一.offset 一般用来检测盒子的偏移.位移,都是只读属性,不能赋值 offsetWidth和offsetHeight表示的是:调用者盒子的宽和高,包括盒子自身的padding和border off ...

  6. .net core 中使用ef 访问mysql

    1.参考文档说修改项目文件添加,就得这么做,不然会报错 <ItemGroup> <DotNetCliToolReference Include="Microsoft.Ent ...

  7. Java8特性之Lambda、方法引用和Streams

    这里涉及三个重要特性: Lambda 方法引用 Streams ① Lambda 最早了解Lambda是在C#中,而从Java8开始,Lambda也成为了新的特性,而这个新的特性的目的,就是为了消除单 ...

  8. SpringMVC学习 十三 拦截器栈

    拦截器栈:就是有多个拦截器同时拦截相同的控制器(controller)请求,这写拦截器就构成了拦截器栈. 栈的特点是先进后出,在拦截器栈中也是如此,如果先执行了preHandle方法,也就是意味着先进 ...

  9. xbeePROS1发送的数据在802.15.4网络中有多大时延?

    完整的计算过程请参考Digi官方网站的文章:Sending data through an 802.15.4 network latency timing. Digi的S1模块可以跑802.15.4固 ...

  10. # 2019-2020-3 《Java 程序设计》第五周学习总结

    2019-2020-3 <Java 程序设计>第五周知识总结 1.使用interface来定义一个接口.接口定义同类的定义类似,也是分为接口的声明和接口体,其中接口体由常量定义和方法定义两 ...