文档章节

String ,  StringBuffer ,  StringBuilder的区别

tsmyk0715
 tsmyk0715
发布于 10/17 22:54
字数 1275
阅读 113
收藏 29

String ,  StringBuffer ,  StringBuilder的区别

String

首先,String 是用来表示一个字符串常量的,它是一个不可变对象,意味着,一旦我们创建了某个字符串之后,就不能再改变它的值了,我们可以从它的源码中看到,它是使用一个 final 的数组来存放内容的,即表示它是一个常量。

    /** The value is used for character storage. */
    private final char value[];

接下来看个例子:

public class Main {

    public static void main(String[] args) {

        String s1 = "a";
        String s2 = "a";
        String s3 = new String("a");
        System.out.println(s1 == s2); // true
        System.out.println(s1 == s3); // false

        String s4 = new String("b");
        String s5 = new String("b");
        System.out.println(s4 == s5); // false

        String s6 = "a" + "b";
        String s7 = "ab";
        System.out.println(s6 == s7); // true

        String s8 = new String("ab");
        System.out.println(s6 == s8); // false
        System.out.println(s7 == s8); //false
    }
}

因为 String 是存放在常量池中的,虚拟机会对其进行优化,上面,虽然声明了 8 个变量,可是在常量池中只存放了 "a","b","ab" 这三个常量,使用 jclasslib 查看可知,如下所示:

声明一个字符串对象,如果能在常量池中找到,则直接把引用指向它即可,找不到才会创建,之后放到常量池中。

由于,字符串是不可变对象,如果多个字符串相加,则会出现什么呢?

        String s1 = "a" + "b" + "c" + "d" + "e";
        System.out.println(s1);

分析字节码:

可以看到,上述字符串相加后,JVM还是一次性把 “abcde”加载到内存中,此时常量池中只有一个字符串变量,即 "abcde",如下所示:

ldc指令:把字符串加载到常量池中(Push item from run-time constant pool),更多JVM指令参考:JVM指令

也就是说 通过 "a" + "b" + "c" + "d" + "e" 和 "abcde" 对于 JVM 来说是一回事,只会加载一次;

但是通过下面这种方式相加会有什么变化呢?

        String s1 = "a";
        String s2 = "b";
        String s3 = "c";
        String s4 = "d";
        String s5 = "e";
        String s6 = s1 + s2 + s3 + s4 + s5;
        System.out.println(s6);

首先,看下,JVM调用几次 ldc 指令加载字符串到常量池中:

再来看看这种方式一个全部的字节码:

 0 ldc #2 <a>
 2 astore_1
 3 ldc #3 <b>
 5 astore_2
 6 ldc #4 <c>
 8 astore_3
 9 ldc #5 <d>
11 astore 4
13 ldc #6 <e>
15 astore 5
17 new #7 <java/lang/StringBuilder>
20 dup
21 invokespecial #8 <java/lang/StringBuilder.<init>>
24 aload_1
25 invokevirtual #9 <java/lang/StringBuilder.append>
28 aload_2
29 invokevirtual #9 <java/lang/StringBuilder.append>
32 aload_3
33 invokevirtual #9 <java/lang/StringBuilder.append>
36 aload 4
38 invokevirtual #9 <java/lang/StringBuilder.append>
41 aload 5
43 invokevirtual #9 <java/lang/StringBuilder.append>
46 invokevirtual #10 <java/lang/StringBuilder.toString>
49 astore 6

前面部分,调用 ldc 指令加载字符串到常量池中,后面部分可以看到,JVM 会对这种字符串相加的方式进行优化,使用 StringBuilder 来进行字符串的拼接,

StringBuffer

StringBuffer 它是一个可变字符串,从源码中可以看到,它用来存放元素的 char 数组没有使用 final 修饰,且,char 数组的初始大小为 16.:

StringBuffer sb = new StringBuffer();
                |
                |
    public StringBuffer() {
        super(16);
    }
                |
                |
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }
                |
                |
         char[] value;
       

之后使用它的 append 方法进行字符串的连接:

        String s1 = "a";
        String s2 = "b";
        String s3 = "c";
        String s4 = "d";
        String s5 = "e";

        StringBuffer sb = new StringBuffer();
        sb.append(s1).append(s2).append(s3).append(s4).append(s5);

字节码如下:

 0 ldc #2 <a>
 2 astore_1
 3 ldc #3 <b>
 5 astore_2
 6 ldc #4 <c>
 8 astore_3
 9 ldc #5 <d>
11 astore 4
13 ldc #6 <e>
15 astore 5
17 new #7 <java/lang/StringBuffer>
20 dup
21 invokespecial #8 <java/lang/StringBuffer.<init>>
24 astore 6
26 aload 6
28 aload_1
29 invokevirtual #9 <java/lang/StringBuffer.append>
32 aload_2
33 invokevirtual #9 <java/lang/StringBuffer.append>
36 aload_3
37 invokevirtual #9 <java/lang/StringBuffer.append>
40 aload 4
42 invokevirtual #9 <java/lang/StringBuffer.append>
45 aload 5
47 invokevirtual #9 <java/lang/StringBuffer.append>
50 pop
51 return

此外,StringBuffer 还是线程安全的,可在多线程下使用,append 方法被 synchronized 修饰,以保证同步:

    public synchronized StringBuffer append(Object obj) {
        toStringCache = null;
        super.append(String.valueOf(obj));
        return this;
    }

StringBuilder

StringBuilder 它和 StringBuffer 一样,都是可变字符串,它用来存放元素的 char 数组没有使用 final 修饰,且,char 数组的初始大小为 16.:

StringBuilder sb = new StringBuilder();
               |
               |
    public StringBuilder() {
        super(16);
    }
               |
               |
 AbstractStringBuilder(int capacity) {
        value = new char[capacity];
 }
               |
               |
          char[] value;

其实,StringBuilder 和 StringBuffer 它们有共同的父类:

只不过 StringBuilder 不是线程安全的,在多线程环境下使用会出现数据不一致的问题。它的 append 方法并没有使用 synchronized 修饰:

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

它通过 append 拼接字符串的字节码和 StringBuffer 的一样。

总结

综上,

String 它是一个不可变的对象,一旦创建就不会改变,String 的相加,JVM 会调用 StringBuilder 的 append 来进行优化。

StringBuffer它是可变字符串,它是线程安全的。

StringBuilder它也是可变字符串,但是它不是线程安全的,它和 StringBuffer 有共同的父类。

由于 StringBuffer 的 append 方法有 synchronized 进行修饰,所以性能较 StringBuilder 的低,如果在单线程下,可使用 StringBuilder。

 

 

© 著作权归作者所有

共有 人打赏支持
上一篇: try-catch-finally
下一篇: hashcode, equals, ==
tsmyk0715
粉丝 34
博文 65
码字总数 143556
作品 0
成都
程序员
私信 提问
ID02 treemap StringBuilder & StringBuffer 

TreeMap:

萧小蚁
2016/09/22
6
0
java中String、StringBuffer、StringBuilder的区别 - jihite

java中String、StringBuffer、StringBuilder的区别 - jihite 博客园jihite2018-01-011 阅读 StringBufferjavaString Java里面提供了String,StringBuffer和StringBuilder三个类来封装字符串 ......

博客园_jihite
01/01
0
0
String,StringBuilder,StringBuffer对比

简述 StringBuilder 对比 StringBuffer StringBuilder是非线程安全 StringBuffer是线程安全的。 StringBuilder和StringBuffer其余的特性都是一样的,他们与String的区别: StringBuilder与S...

Real_man
03/20
0
0
【翻译】Java中String, StringBuffer, StringBuilder的区别

String 是 Java 中最重要的类之一,并且任何刚开始做Java编程的人,都会用String定义一些内容,然后通过著名的System.out.println()语句来打印出来。 然而,很多Java新手都不会意识到 String...

YuanyuanL
2014/09/03
0
4
LearnJava(三)String、StringBuffer 与 StringBuilder

  我们知道,String对象是不可变的,而Java中String类提供了“+”进行字符串拼接操作,从JDK1.5开始,字符串的拼接操作是通过StringBuffer类来完成的。 上述代码的实际实现过程是:   也...

laughter_jiang
07/29
0
0

没有更多内容

加载失败,请刷新页面

加载更多

利用ibeetl 实现selectpicker 的三级联动

1. js 直接写在html页面上面,ibeetl 就可以动态地利用后台传上来的model List ,不需要每次点击都要ajax请求后台 2. 使用selectpicker 的时候,除了对selecct option的动态处理后,还需要 $("#...

donald121
15分钟前
0
0
Android SELinux avc dennied权限问题解决方法

1. 概述 SELinux是Google从android 5.0开始,强制引入的一套非常严格的权限管理机制,主要用于增强系统的安全性。 然而,在开发中,我们经常会遇到由于SELinux造成的各种权限不足,即使拥有“...

TreasureWe
26分钟前
1
0
阿里云ACP认证详细笔记(一)

ECS--------------------------1.云服务器Elastic Compute Service(ECS)2.Terraform:您可以使用开源工具Terraform来预配和管理ECS资源。Terraform提供一种简单机制,能够将配置文件部署...

啃不动地大坚果
31分钟前
0
0
如何实现MetaMask签名授权后DAPP一键登录功能?

1 摘要 网站太多,各种用户名/密码实在记不住。所以我们逐渐接受了BAT账号的授权登录功能。在以太坊DAPP应用中,也可以使用MetaMask实现授权后一键登录功能。MetaMask是去中心化钱包,授权信...

HiBlock
32分钟前
1
0
raspberrypi的相关网址

一、NOOBS安装 NOOBS使用说明书 http://www.shumeipai.net/thread-20009-1-1.html NOOBS自定义多系统启动 https://www.jianshu.com/p/afbcd17b785d NOOBS安装自定义系统 https://blog.csdn.......

mbzhong
33分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部