该题来自华为。 
        字符串是Java程序中最常用的数据结构之一。在Java中String类已经重载了"+"。也就是说,字符串可以直接
使用"+"进行连接,如下面代码所示:

1.String s = "abc" + "ddd"; 

但这样做真的好吗?当然,这个问题不能简单地回答 yes or no。要根据具体情况来定。在 Java 中提供了一个
StringBuilder类(这个类只在J2SE5及以上版本提供,以前的版本使用StringBuffer类),这个类也可以起到"+"的
作用。那么我们应该用哪个呢? 
下面让我们先看看如下的代码:

1.package string;
2.
3.   public class TestSimplePlus
4.   {
5.       public static void main(String[] args)
6.       {
7.           String s = "abc";
8.           String ss = "ok" + s + "xyz" + 5;
9.           System.out.println(ss);
10.       }
11.   }

上面的代码将会输出正确的结果。从表面上看,对字符串和整型使用"+"号并没有什么区别,但事实真的如此吗?
下面让我们来看看这段代码的本质。 
我们首先使用反编译工具(如jdk带的javap、或jad)将 TestSimplePlus反编译成Java Byte Code,其中的奥
秘就一目了然了。在本文将使用jad来反编译,命令如下: 
jad -o -a -s d.java TestSimplePlus.class 
反编译后的代码如下:

1. package string;
2.
3. import java.io.PrintStream;
4.
5. public class TestSimplePlus
6. {
7.     public TestSimplePlus()
8.     {
9.     //    0    0:aload_0
10.     //    1    1:invokespecial   #8   <Method void Object()>
11.     //    2    4:return
12.     }
13.
14.     public static void main(String args[])
15.     {
16.       String s = "abc";
17.     //    0    0:ldc1            #16  <String "abc">
18.     //    1    2:astore_1
19.       String ss = (new StringBuilder("ok")).append(s).append("xyz").append(5).toString();
20.     //    2    3:new             #18  <Class StringBuilder>
21.     //    3    6:dup
22.     //    4    7:ldc1            #20  <String "ok">
23.     //    5    9:invokespecial   #22  <Method void StringBuilder(String)>
24.     //    6   12:aload_1
25.     //    7   13:invokevirtual   #25  <Method StringBuilder StringBuilder.append(String)>
26.     //    8   16:ldc1            #29  <String "xyz">
27.     //    9   18:invokevirtual   #25  <Method StringBuilder StringBuilder.append(String)>
28.     //   10   21:iconst_5
29.     //   11   22:invokevirtual   #31  <Method StringBuilder StringBuilder.append(int)>
30.     //   12   25:invokevirtual   #34  <Method String StringBuilder.toString()>
31.     //   13   28:astore_2
32.       System.out.println(ss);
33.     //   14   29:getstatic       #38  <Field PrintStream System.out>
34.     //   15   32:aload_2
35.     //   16   33:invokevirtual   #44  <Method void PrintStream.println(String)>
36.     //   17   36:return
37.     }
38. }

读者可能看到上面的Java字节码感到迷糊,不过大家不必担心。本文的目的并不是讲解Java Byte Code,因此,
并不用了解具体的字节码的含义。 
使用 jad 反编译的好处之一就是可以同时生成字节码和源代码。这样可以进行对照研究。从上面的代码很容易看
出,虽然在源程序中使用了"+",但在编译时仍然将"+"转换成StringBuilder。因此,我们可以得出结论,在 Java 中
无论使用何种方式进行字符串连接,实际上都使用的是 StringBuilder。 
那么是不是可以根据这个结论推出使用"+"和StringBuilder的效果是一样的呢?这个要从两个方面的解释。如果
从运行结果来解释,那么"+"和StringBuilder是完全等效的。但如果从运行效率和资源消耗方面看,那它们将存在很
大的区别。 
  当然,如果连接字符串行表达式很简单(如上面的顺序结构),那么"+"和StringBuilder基本是一样的,但如果
结构比较复杂,如使用循环来连接字符串,那么产生的 Java Byte Code 就会有很大的区别。先让我们看看如下的代
码:

1. package string;
2.
3.   import java.util.*;
4.
5.   public class TestComplexPlus
6.   {
7.       public static void main(String[] args)
8.       {
9.           String s = "";
10.           Random rand = new Random();
11.           for (int i = 0; i < 10; i++)
12.           {
13.               s = s + rand.nextInt(1000) + " ";
14.           }
15.           System.out.println(s);
16.       }
17.   }

上面的代码返编译后的 Java Byte Code 如下:

1. package string;
2.
3. import java.io.PrintStream;
4. import java.util.Random;
5.
6. public class TestComplexPlus
7. {
8.
9.     public TestComplexPlus()
10.     {
11.     //    0    0:aload_0
12.     //    1    1:invokespecial   #8   <Method void Object()>
13.     //    2    4:return
14.     }
15.
16.     public static void main(String args[])
17.     {
18.         String s = "";
19.     //    0    0:ldc1            #16  <String "">
20.     //    1    2:astore_1
21.         Random rand = new Random();
22.     //    2    3:new             #18  <Class Random>
23.     //    3    6:dup
24.     //    4    7:invokespecial   #20  <Method void Random()>
25.     //    5   10:astore_2
26.         for(int i = 0; i < 10; i++)
27.     //*   6   11:iconst_0
28.     //*   7   12:istore_3
29.     //*   8   13:goto            49
30.          s = (new StringBuilder(String.valueOf(s))).append(rand.nextInt(1000)).append(" ").toString();
31.     //    9   16:new             #21  <Class StringBuilder>
32.     //   10   19:dup
33.     //   11   20:aload_1
34.     //   12   21:invokestatic    #23  <Method String String.valueOf(Object)>
35.     //   13   24:invokespecial   #29  <Method void StringBuilder(String)>
36.     //   14   27:aload_2
37.     //   15   28:sipush          1000
38.     //   16   31:invokevirtual   #32  <Method int Random.nextInt(int)>
39.     //   17   34:invokevirtual   #36  <Method StringBuilder StringBuilder.append(int)>
40.     //   18   37:ldc1            #40  <String " ">
41.     //   19   39:invokevirtual   #42  <Method StringBuilder StringBuilder.append(String)>
42.     //   20   42:invokevirtual   #45  <Method String StringBuilder.toString()>
43.     //   21   45:astore_1
44.
45.     //   22   46:iinc            3  1
46.     //   23   49:iload_3
47.     //   24   50:bipush          10
48.     //   25   52:icmplt          16
49.         System.out.println(s);
50.     //   26   55:getstatic       #49  <Field PrintStream System.out>
51.     //   27   58:aload_1
52.     //   28   59:invokevirtual   #55  <Method void PrintStream.println(String)>
53.     //   29   62:return
54.     }
55. }

大家可以看到,虽然编译器将"+"转换成了StringBuilder,但创建StringBuilder对象的位置却在for语句内
部。这就意味着每执行一次循环,就会创建一个StringBuilder对象(对于本例来说,是创建了10个StringBuilder
对象),虽然Java有垃圾回收器,但这个回收器的工作时间是不定的。如果不断产生这样的垃圾,那么仍然会占用
大量的资源。解决这个问题的方法就是在程序中直接使用StringBuilder来连接字符串,代码如下:

1. package string;
2.
3. import java.util.*;
4.
5. public class TestStringBuilder
6. {
7.     public static void main(String[] args)
8.     {
9.         String s = "";
10.         Random rand = new Random();
11.         StringBuilder result = new StringBuilder();
12.         for (int i = 0; i < 10; i++)
13.         {
14.             result.append(rand.nextInt(1000));
15.             result.append(" ");
16.         }
17.         System.out.println(result.toString());
18.     }
19. }

上面代码反编译后的结果如下:

1. 20.package string;
2.
3. import java.io.PrintStream;
4. import java.util.Random;
5.
6. public class TestStringBuilder
7. {
8.
9.     public TestStringBuilder()
10.     {
11.     //    0    0:aload_0
12.     //    1    1:invokespecial   #8   <Method void Object()>
13.     //    2    4:return
14.     }
15.
16.     public static void main(String args[])
17.     {
18.         String s = "";
19.     //    0    0:ldc1            #16  <String "">
20.     //    1    2:astore_1
21.         Random rand = new Random();
22.     //    2    3:new             #18  <Class Random>
23.     //    3    6:dup
24.     //    4    7:invokespecial   #20  <Method void Random()>
25.     //    5   10:astore_2
26.         StringBuilder result = new StringBuilder();
27.     //    6   11:new             #21  <Class StringBuilder>
28.     //    7   14:dup
29.     //    8   15:invokespecial   #23  <Method void StringBuilder()>
30.     //    9   18:astore_3
31.         for(int i = 0; i < 10; i++)
32.     //*  10   19:iconst_0
33.     //*  11   20:istore          4
34.     //*  12   22:goto            47
35.         {
36.             result.append(rand.nextInt(1000));
37.     //   13   25:aload_3
38.     //   14   26:aload_2
39.     //   15   27:sipush          1000
40.     //   16   30:invokevirtual   #24  <Method int Random.nextInt(int)>
41.     //   17   33:invokevirtual   #28  <Method StringBuilder StringBuilder.append(int)>
42.     //   18   36:pop
43.             result.append(" ");
44.     //   19   37:aload_3
45.     //   20   38:ldc1            #32  <String " ">
46.     //   21   40:invokevirtual   #34  <Method StringBuilder StringBuilder.append(String)>
47.     //   22   43:pop
48.         }
49.
50.     //   23   44:iinc            4  1
51.     //   24   47:iload           4
52.     //   25   49:bipush          10
53.     //   26   51:icmplt          25
54.         System.out.println(result.toString());
55.     //   27   54:getstatic       #37  <Field PrintStream System.out>
56.     //   28   57:aload_3
57.     //   29   58:invokevirtual   #43  <Method String StringBuilder.toString()>
58.     //   30   61:invokevirtual   #47  <Method void PrintStream.println(String)>
59.     //   31   64:return
60.     }
61. } 

从上面的反编译结果可以看出,创建StringBuilder的代码被放在了for语句外。虽然这样处理在源程序中看起
来复杂,但却换来了更高的效率,同时消耗的资源也更少了。 
在使用StringBuilder时要注意,尽量不要"+"和StringBuilder混着用,否则会创建更多的StringBuilder对
象,如下面代码所:

for (int i = 0; i < 10; i++)     {         result.append(rand.nextInt(1000));         result.append(" ");    } 

改成如下形式:

for (int i = 0; i < 10; i++){      result.append(rand.nextInt(1000) + " "); }

则反编译后的结果如下:

 for(int i = 0; i < 10; i++)  //*  10   19:iconst_0           //*  11   20:istore          4   //*  12   22:goto            65
{    result.append((new StringBuilder(String.valueOf(rand.nextInt(1000)))).append(" ").toString()); //   13   25:aload_3           //   14   26:new             #21  <Class StringBuilder>  //   15   29:dup         

从上面的代码可以看出,Java编译器将"+"编译成了StringBuilder,这样for语句每循环一次,又创建了一个
StringBuilder对象。 
    如果将上面的代码在JDK1.4下编译,必须将StringBuilder改为StringBuffer,而JDK1.4将"+"转换为
StringBuffer(因为JDK1.4并没有提供StringBuilder类)。StringBuffer和StringBuilder的功能基本一样,只是
StringBuffer是线程安全的,而StringBuilder不是线程安全的。因此,StringBuilder的效率会更高。

String“+”与StringBuffer/StringBuilder 对象的append方法相关推荐

  1. #StringBuilder中的append方法 @FDDLC

    StringBuilder中 的append方法有很多重载形式: 核心实现在其父类AbstractStringBuilder中. 一.public StringBuilder append(char ...

  2. 9.String类 StringBuffer/StringBuilder 的定义

    String类:表示一个字符串,是引用数据类型 1.字符串存储格式:字符串用final修饰的char类型数组进行存储的 2.String的不可变性:字符串一旦初始化不可更改,但是可以通过在字符串常量池 ...

  3. StringBuffer类中的append方法

    StringBuffer类中的方法主要偏重于对于字符串的变化,例如追加.插入和删除等,这个也是StringBuffer和String类的主要区别. append方法 public StringBuff ...

  4. 面试积累(String和StringBuffer, StringBuilder的理解)

    1.String是不可变的对象,在每次对String类型进行改变的时候其实都是等同于生成了一个新的String对象,然后指向新的String对象,所以经常改变内容的字符串用String类型的话,就会对 ...

  5. 比较String、StringBuffer和StringBuilder

    在Java 中一共提供了String.StringBuffer.StringBuilder 用来表示和操作字符串,对于字符串的处理我们经常在项目中可以使用的到.了解它们底层的实现可以帮我们正确的使用这 ...

  6. Java中String、StringBuffer和StringBuilder的区别

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6581009.html  在编程中,对于字符串拼接,我们可以用String类重载的+或concat(str).S ...

  7. 面试中关于String、StringBuffer、StringBuilder的频率最高问题

    1.String类的特性 String类 是final修饰的,不可以被继承. String类的底层是基于char数组的. 安全性: 对象都是只读的,所以多线程并发访问也不会有任何问题. 由于不可变,用 ...

  8. 关于String,StringBuffer和StringBuilder之间的区别和联系

    本文来说下关于String,StringBuffer和StringBuilder之间的区别和联系 文章目录 概述 String 和 StringBuffer StringBuilder 本文小结 概述 ...

  9. 3.4 java基础总结①常用类String②Stringbuffer③Stringbuilder

    ①常用类String②正则表达式③Stringbuffer④Stringbuilder 一.String ①String内存分析 String str0 = "hello";预先放 ...

最新文章

  1. python【蓝桥杯vip练习题库】ADV-17统计单词数
  2. 微软重新开源 MS-DOS 1.25/2.0:已诞生 36 年
  3. 虚拟form 下载文件
  4. 分布式机器学习框架:MxNet
  5. 用户登录成功后才进入主窗口进行其他操作
  6. pytorch从dataframe中提取信息,变为可训练的tensor
  7. 华为机试HJ1:字符串最后一个单词的长度
  8. Kafka从上手到实践 - Kafka CLI:Consumer CLI Producer CLI | 凌云时刻
  9. Python实现向量自回归(VAR)模型——完整步骤
  10. 我的2015技术学习流水账
  11. usb无线网卡安装在服务器上,USB无线网卡怎么用?USB无线网卡如何安装?
  12. 2020年十大数字客户体验(CX)软件平台
  13. Admob设置Android设备为测试设备(2020年5月7日亲测有效)
  14. Barefoot P4加速SDN
  15. 微信支付宝,个人支付收款接口现状剖析
  16. syslog server配置与logrotate的配置与理解
  17. 2022海康威视秋招内推(最新)
  18. 项目进度管理研究综述
  19. 上海成立司法行政大数据实验室
  20. 一篇文章教会你利用Python网络爬虫实现豆瓣电影采集

热门文章

  1. strcpy和strncpy的区别
  2. vot-toolkit-python测试DiMP50在VOT2018上的表现
  3. 华为服务器查询IPMI地址
  4. oracle获取当前时间
  5. linux可以打开浏览器嘛,Linux下怎样可以打开浏览器?
  6. 架构师技术选型所需要考虑的要素
  7. 局域网socket传输视频流
  8. Jekins安装和部署
  9. PHP接口API文档转换SDK【神器】
  10. Linux之更改配置文件永久修改IP地址