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

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

一、String

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

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进行了赋值。看下面的例子:

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文件:

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途径去自动转。)看下面的代码:

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文件:

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的源码:

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的源码如下:

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。

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

  1. java stringbuffer原理_深入理解Java:String

    在讲解String之前,我们先了解一下Java的内存结构. 一.Java内存模型 按照官方的说法:Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配. JVM主要管理两 ...

  2. c#中的string和StringBuilder的区别

    string和StringBuilder的区别 解析c#中的string和StringBuilder的区别,只有非常了解后我们才能更好的编程和灵活使用. 区别 1.类 String 表示字符串, 比如 ...

  3. string 与stringbuilder的区别

    string 与stringbuilder的区别 C#   String   对象是不可改变的.每次使用   System.String   类中的方法之一时,都要在内存中创建一个新的字符串对象,这就 ...

  4. java 常量折叠_深入理解Java虚拟机之早期编译器优化

    Javac编译器 Javac编译器是一个由Java语言编写的程序 Javac的源码与调试 从Sun Javac的代码来看,编译器大致分为3个过程: 解析与填充符号表的过程 插入式注解处理器的注解处理过 ...

  5. String和StringBuilder的区别

    1 String和StringBuilder拼接字符串的区别 总的来说吧,String类的内容是不可变的,StringBuilder的内容是可变的 有好长一段时间都很迷这个String和StringB ...

  6. String 和stringBuilder的区别

    string和stringbuilder对象都可以很方便的处理字符串: string是引用类型,在堆上分配内存.string对象一旦创建变不能再改变.在用算时会产生一个新的实例. Stringbuil ...

  7. java装箱与拆箱原理_深入理解Java中的装箱和拆箱

    前言 自动装箱和拆箱问题是Java中一个老生常谈的问题了,今天我们就来一些看一下装箱和拆箱中的若干问题.本文先讲述装箱和拆箱最基本的东西,再来看一下面试笔试中经常遇到的与装箱.拆箱相关的问题. 若有不 ...

  8. 微博java实现原理_【转】URL短地址压缩算法 微博短地址原理解析 (Java实现)...

    最近,项目中需要用到短网址(ShortUrl)的算法,于是在网上搜索一番,发现有C#的算法,有.Net的算法,有PHP的算法,就是没有找到Java版的短网址(ShortUrl)的算法,很是郁闷.同时还 ...

  9. java foreach 原理_一不小心就让Java开发者踩坑的failfast是个什么鬼?

    1 什么是fail-fast 首先我们看下维基百科中关于fail-fast的解释: 在系统设计中,快速失效系统一种可以立即报告任何可能表明故障的情况的系统.快速失效系统通常设计用于停止正常操作,而不是 ...

最新文章

  1. 2022-2028年中国喷涂速凝橡胶行业市场调研分析及未来前景分析报告
  2. 单目视觉里程计性能估计
  3. scala函数式编程(二) scala基础语法介绍
  4. 161026、更快速将你的页面展示给用户[前端优化篇]
  5. MySQL Percona Toolkit--pt-osc与online DDL选择
  6. 两组声音的一维数据如何比较相似度_TSNE高维数据降维可视化工具 入门到理解 + python实现...
  7. SDWebImage缓存图片的机制(转)
  8. 中国手机摄像头产业链
  9. DPDK Release 22.07
  10. python股票成交明细_AkShare-股票数据-龙虎榜-机构席位成交明细
  11. 如何修改linux启动项目路径,Jetty配置虚拟目录,实现把web项目发布到自定义目录,指定指定上下文访问;jetty编码修改;Jetty加入Linux service实现开机自启动...
  12. 北理大编程作业:确定母亲节
  13. Mixed Precision Training混合精度训练笔记及框架代码
  14. js中浏览器失焦获焦的几种结局方法
  15. Alpha版本——展示博客【第二组】
  16. jquery中的各种动画效果
  17. css背景图铺满整个屏幕
  18. 2019年个人总结,写在人生不惑之年
  19. 契约锁android代码,契约锁怎么签合同
  20. 外语学习的真实方法与误区19

热门文章

  1. mysql 出现 quot_mysql 出现 quot;the table is fullquot;的问题 - tmuffamd - ITeye博客
  2. 玩转SpringBoot2.x之缓存对象
  3. Win10 通过 VirtualBox安装CentOS7操作手册
  4. win8安装mysql出现2503_win8.1安装msi文件出现2503错误的解决方法
  5. 基于JAVA+SpringMVC+MYSQL的求职招聘管理系统
  6. linux内核模块签名,linux内核模块签名
  7. my stackoverflow
  8. Asp.net Mvc使用PagedList分页
  9. Linux 添加环境变量
  10. 分形之希尔伯特-皮亚诺(Hilbert-Peano)曲线