String“+”与StringBuffer/StringBuilder 对象的append方法
该题来自华为。
字符串是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方法相关推荐
- #StringBuilder中的append方法 @FDDLC
StringBuilder中 的append方法有很多重载形式: 核心实现在其父类AbstractStringBuilder中. 一.public StringBuilder append(char ...
- 9.String类 StringBuffer/StringBuilder 的定义
String类:表示一个字符串,是引用数据类型 1.字符串存储格式:字符串用final修饰的char类型数组进行存储的 2.String的不可变性:字符串一旦初始化不可更改,但是可以通过在字符串常量池 ...
- StringBuffer类中的append方法
StringBuffer类中的方法主要偏重于对于字符串的变化,例如追加.插入和删除等,这个也是StringBuffer和String类的主要区别. append方法 public StringBuff ...
- 面试积累(String和StringBuffer, StringBuilder的理解)
1.String是不可变的对象,在每次对String类型进行改变的时候其实都是等同于生成了一个新的String对象,然后指向新的String对象,所以经常改变内容的字符串用String类型的话,就会对 ...
- 比较String、StringBuffer和StringBuilder
在Java 中一共提供了String.StringBuffer.StringBuilder 用来表示和操作字符串,对于字符串的处理我们经常在项目中可以使用的到.了解它们底层的实现可以帮我们正确的使用这 ...
- Java中String、StringBuffer和StringBuilder的区别
转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6581009.html 在编程中,对于字符串拼接,我们可以用String类重载的+或concat(str).S ...
- 面试中关于String、StringBuffer、StringBuilder的频率最高问题
1.String类的特性 String类 是final修饰的,不可以被继承. String类的底层是基于char数组的. 安全性: 对象都是只读的,所以多线程并发访问也不会有任何问题. 由于不可变,用 ...
- 关于String,StringBuffer和StringBuilder之间的区别和联系
本文来说下关于String,StringBuffer和StringBuilder之间的区别和联系 文章目录 概述 String 和 StringBuffer StringBuilder 本文小结 概述 ...
- 3.4 java基础总结①常用类String②Stringbuffer③Stringbuilder
①常用类String②正则表达式③Stringbuffer④Stringbuilder 一.String ①String内存分析 String str0 = "hello";预先放 ...
最新文章
- python【蓝桥杯vip练习题库】ADV-17统计单词数
- 微软重新开源 MS-DOS 1.25/2.0:已诞生 36 年
- 虚拟form 下载文件
- 分布式机器学习框架:MxNet
- 用户登录成功后才进入主窗口进行其他操作
- pytorch从dataframe中提取信息,变为可训练的tensor
- 华为机试HJ1:字符串最后一个单词的长度
- Kafka从上手到实践 - Kafka CLI:Consumer CLI Producer CLI | 凌云时刻
- Python实现向量自回归(VAR)模型——完整步骤
- 我的2015技术学习流水账
- usb无线网卡安装在服务器上,USB无线网卡怎么用?USB无线网卡如何安装?
- 2020年十大数字客户体验(CX)软件平台
- Admob设置Android设备为测试设备(2020年5月7日亲测有效)
- Barefoot P4加速SDN
- 微信支付宝,个人支付收款接口现状剖析
- syslog server配置与logrotate的配置与理解
- 2022海康威视秋招内推(最新)
- 项目进度管理研究综述
- 上海成立司法行政大数据实验室
- 一篇文章教会你利用Python网络爬虫实现豆瓣电影采集