String中的字符串拼接问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/cugwuhan2014/article/details/78038254

先来看两段段简短的代码:
示例1

```java public String nullStringTest(){ String s = null; s += "abc"; return s; } ```

示例2

```java public void stringPool(){ String a="hello"; String b="hell"; String c=b+"o"; String d="hell"+"o";
System.out.println(a == b+new String("o"));
System.out.println(a == c);
System.out.println(a == d);

}

</textarea>
打印结果各是什么?

##一、String + String 的本质

***示例1***的反编译结果如下:

>注:java的反编译指令 *javap  -verbose*  *类名.class*

 <textarea readonly="readonly" name="code" class="asm">
```asm
0: aconst_null
1: astore_1
2: new           #5       // class java/lang/StringBuilder
5: dup
6: invokespecial #6      // Method java/lang/StringBuilder."<init>":()V
9: aload_1
10: invokevirtual #7     // Method java/lang/StringBuilder.append:      (Ljava/lang/String;)Ljava/lang/StringBuilder;
13: ldc           #8     // String abc
15: invokevirtual #7     // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
18: invokevirtual #9   // Method java/lang/StringBuilder.toString:   	()Ljava/lang/String;
21: astore_1
22: aload_1
23: areturn
由反编译代码可以看出,代码 ```java s += "abc"; ``` 等价于 ```java StringBuilder tmp = new StringBuilder(); tmp.append(String.valueOf((Object)null)); tmp.append("abc"); s = tmp.toString();
而`String`的`valueOf(Object ojb)`方法实现源码如下:
![这里写图片描述](https://img-blog.csdn.net/20170920105707037?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3Vnd3VoYW4yMDE0/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
因此***示例1***的结果为`nullabc`
![这里写图片描述](https://img-blog.csdn.net/20170920105856637?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3Vnd3VoYW4yMDE0/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)

##二、String对象的存储
***示例2***的反编译代码比较长,就不贴出了,有兴趣的朋友自行反编译出来研读。在分析***示例2***的结果之前,先来看一个更简单的***示例3***:
```java
String str1 = "abc";
String str2 = new String("abcd");

***示例3***的内存分配情况如下图:
这里写图片描述
String str1 = "abc";先有字符串"abc"存在于常量池,然后Java栈上的str1执行常量池字符串"abc"
String str2 = new String("abcd");先有字符串"abcd"放入常量池,然后new了一份字符串"abcd"放入Java堆(字符串常量"abcd"在编译期就已经确定放入常量池,而Java堆上的"abcd"是在运行期初始化阶段才确定,因此先有常量池"abcd",再有Java堆”abcd”),然后Java栈的str2指向Java堆的"abcd"
如果***示例3***的内存分配可以理解,就不难得出***示例2***的内存分配情况:
这里写图片描述
***示例2***的执行结果为:
这里写图片描述
思考:String类为何设计为不可变?

##三、String、StringBuild、StringBuffer的区别
String是字符串常量的引用,String += String的本质是new了新的临时对象StringBuild,拼接后再StringBuild.toString赋给原String。所有大量字符串拼接不要直接使用String,否则会生成大量临时对象,严重影响性能。
StringBuild进行字符串拼接不会生成临时对象,效率高,但不是线程安全的。
StringBuffer进行字符串拼接也不会生成临时对象,效率略低于StringBuild,线程安全。
***示例4***是以上三种类进行大量字符串拼接的示例代码:

private static int LOOP_TIMES = 10000;
private Random ran = new Random();

public void loopString(){
    String string = "";
    for (int i=0; i<LOOP_TIMES; i++){
        string += ran.nextInt();
    }
}

public void loopStringBuild(){
    StringBuilder stringBuilder = new StringBuilder();
    for (int i=0; i<LOOP_TIMES; i++){
        stringBuilder.append(ran.nextInt());
    }
}

public void loopStringBuffer(){
    StringBuffer stringBuffer = new StringBuffer();
    for (int i=0; i<LOOP_TIMES; i++){
        stringBuffer.append(ran.nextInt());
    }
}

执行结果:

I/MainActivity: loopString -->18435ms
I/MainActivity: loopStringBuild -->10ms
I/MainActivity: loopStringBuffer -->10ms

##总结
String的+操作是一种语法糖,其本质是创建了临时的StringBuild对象进行append操作,然后toString()赋给原来的String引用,因此大量字符串拼接不要直接用String,应该使用StringBuild或StringBuffer,其中StringBuild不考虑线程同步,效率更高,StringBuffer考虑线程安全,效率略低于StringBuild。

猜你喜欢

转载自blog.csdn.net/cugwuhan2014/article/details/78038254