从内存方面解释Java中String与StringBuilder的性能差异_java

以前经常在网上看到关于Java字符串拼接等方面的讨论。看到有些Java开发人员在给新手程序员的建议中类似如下写道:

不要使用+号拼接字符串,要使用StringBuffer或StringBuilder的append()方法来拼接字符串。
不过,用+号拼接字符串就真的那么令人讨厌,难道使用+号拼接字符串就没有一点可取之处吗?

通过查阅Java API文档中关于String类的部分内容,我们可以看到如下片段:
“Java 语言提供对字符串串联符号("+")以及将其他对象转换为字符串的特殊支持。字符串串联是通过 StringBuilder(或 StringBuffer)类及其 append 方法实现的。字符串转换是通过 toString 方法实现的,该方法由 Object 类定义,并可被 Java中的所有类继承。”

这段话很明确地告诉我们,在Java中使用+号拼接字符串,实际上使用的就是StringBuffer或StringBuilder及其append方法来实现的。

除了Java API文档,我们还可以使用工具查看class类文件的字节码命令来得到上述答案。 例如代码:

public static void main(String[] args) {
  String a = "Hello";
  String b = " world";
  String str = a + b + " !";
  System.out.println(str);
}

通过工具查看到其对应的字节码命令如下:

从字节码命令中,我们可以清楚地看到,我们编写的如下代码

String str = a + b + " !";

被编译器转换成了类似如下语句:

String str = new StringBuilder(String.valueOf(a)).append(b).append(" !").toString();

不仅如此,Java的编译器也是一个比较聪明的编译器,当+号拼接的全部是字符串字面量时,Java的编译器将会在编译时智能地将其转换为一个完整的字符串。例如:

public static void main(String[] args) {
  String str = "Hello" + " world" + ", Java!";
  System.out.println(str);
}

Java编译器直接将这种全是字面量的字符串拼接,在编译时就转换为了一个完整的字符串。

就算+号拼接的字符串中存在变量,Java编译器也会将最前面的字符串字面量合并为一个字符串。

public static void main(String[] args) {
  String java = ", Java!";
  String str = "Hello" + " world" + java;
  System.out.println(str);
}

从上述可知,对于类似String str = str1 + str2 + str3 + str4,这种将多个字符串一次性拼接的操作,使用+号来进行拼接是完全没有问题的。

在Java中,String对象是不可变的(Immutable)。在代码中,可以创建多个某一个String对象的别名。但是这些别名都是的引用是相同的。
比如s1和s2都是”droidyue.com”对象的别名,别名保存着到真实对象的引用。所以s1 = s2

String s1 = "droidyue.com";
String s2 = s1;
System.out.println("s1 and s2 has the same reference =" + (s1 == s2));

并且在Java中,唯一被重载的运算符就是字符串的拼接相关的。+,+=。除此之外,Java设计者不允许重载其他的运算符。

在Java中,唯一被重载的运算符就是字符串的拼接相关的。+,+=。除此之外,Java设计者不允许重载其他的运算符。

众所周知,在Java 1.4版本之前,字符串拼接可以使用StringBuffer,从Java 1.5开始,我们可以使用StringBuilder来拼接字符串。StringBuffer和StringBuilder的主要区别在于:StringBuffer是线程安全的,适用于多线程操作字符串;StringBuilder是线程不安全的,适合单线程下操作字符串。不过,我们的大多数字符串拼接操作都是在单线程下进行的,因此使用StringBuilder有利于提高性能。

在Java 1.4之前,编译器使用StringBuffer来处理+号拼接的字符串;从Java 1.5开始,编译器大多数情况下都使用StringBuilder来处理+号拼接的字符串。

当我们在JDK 1.4的环境下编写代码时,对于上述这种一次性拼接多个字符串的情况,建议最好使用+号来处理。这样,当JDK 升级到1.5及以上版本时,编译器将会自动将其转换为StringBuilder来拼接字符串,从而提高字符串拼接效率。

当然,推荐使用+号拼接字符串也仅限于在一条语句中拼接多个字符串时使用。如果分散在多条语句中拼接一个字符串,仍然建议使用StringBuffer或StringBuilder。 例如:

public static void main(String[] args) {
  String java = ", Java!";
  String str = "";
  str += "Hello";
  str += " world";
  str += java;
  System.out.println(str);
}

编译器编译后的字节命令如下:

从上面的图片中我们可以知道,每一条+号拼接语句,都创建了一个新的StringBuilder对象。这种情况在循环条件下表现得尤其明显,造成了相对较大的性能损耗。因此,在多条语句中拼接字符串,强烈建议使用StringBuffer或StringBuilder来处理。

关于使用StringBuilder带来的优化

此外,在使用StringBuffer或StringBuilder的时候,我们还可以使用如下方式,进一步提高性能(下面代码以StringBuilder为例,StringBuffer与此类似)。

1.预测最终获得的字符串的最大长度。

StringBuilder内部char数组的默认长度为16,当我们append追加字符串后超过此长度时,StringBuilder会扩大内部的数组容量以满足需要。在这个过程中,StringBuilder会创建一个新的较大容量的char数组,并将原数组中的数据复制到新数组中。如果我们能够大致预测到最终拼接得到的字符串的最大长度,就可以在创建StringBuilder对象时指定合适大小的初始容量。例如,我们需要拼接获得含有100个字母a的字符串。即可编写如下代码:

StringBuilder sb = new StringBuilder(100);
for (int i = 0; i < 100; i++) {
  sb.append('a');
}
System.out.println(sb);

请根据实际情况进行平衡,以创建适合初始容量的StringBuilder。

2.对于单个字符,尽可能地使用char类型,而不是String类型。

有些时候,我们需要在字符串后追加单个字符(例如:a),此时应尽可能地使用

sb.append('a');

而不是使用:

sb.append("a");

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索java
, string
stringbuilder
stringbuilder性能、stringbuilder、stringbuilder 清空、c stringbuilder、java stringbuilder,以便于您获取更多的相关知识。

时间: 2024-12-30 16:51:57

从内存方面解释Java中String与StringBuilder的性能差异_java的相关文章

java中String与StringBuilder的区别_java

相信大家对 String 和 StringBuffer 的区别也已经很了解了,但是估计还是会有很多同志对这两个类的工作原理有些不清楚的地方,今天我在这里重新把这个概念给大家复习一下,顺便牵出 J2SE 5.0 里面带来的一个新的字符操作的类-- StringBuilder (先别忙着扔我砖头,我还算清醒,我这里说的不是 C #, Java 也有 StringBuilder 类).那么这个 StringBuilder 和 StringBuffer 以及我们最早遇见的 String 类有那些区别呢?

浅谈java中String StringBuffer StringBuilder的区别_java

* String类是不可变类,只要对String进行修改,都会导致新的对象生成. * StringBuffer和StringBuilder都是可变类,任何对字符串的改变都不会产生新的对象. 在实际使用时,如果经常需要对一个字符串进行修改,例如插入.删除等 * 但StringBuffer和StringBuilder有什么区别呢? StringBuffer是线程安全的,在多线程程序中是很方便使用的,但是程序的效率就会慢一点. StringBuilder不是线程安全的,在单线程中,比StringBuf

java中String的一些方法深入解析_java

1.public String(char[] c,begin,length).从字符数组c的下标begin处开始,将长度为length的字符数组转换为字符串.begin与length可以省略,即将字符数组c转换为字符串.另:字符数组可改为字节数组byte[] b.char[] c=new char[]{'j','y','6','a','4','t','9'}; String s1=new String(c); String s=new String(c,2,3); System.out.prin

Java中String、StringBuffer、StringBuilder的区别详解_java

 java中String.StringBuffer.StringBuilder是编程中经常使用的字符串类,他们之间的区别也是经常在面试中会问到的问题.现在总结一下,看看他们的不同与相同. 1.可变与不可变 String类中使用字符数组保存字符串,如下就是,因为有"final"修饰符,所以可以知道string对象是不可变的. private final char value[]; StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在

java中String数组问题,String[3][2]是表示有6个对象吗?

问题描述 java中String数组问题,String[3][2]是表示有6个对象吗? String[ ][ ] zhuangtai=new String[3][2]; 是表示有6种不同的状态吗,如何理解呢?新手提问,莫见笑 解决方案 上面的错了,应该都是从00开始 这是一个二维数字,里面可以存储6个属性 分别为[0][0] [0][1] [1][0] [1][1] [2][0] [2][1] 你可以看下我这篇文章http://blog.csdn.net/zhanggang740/article

string-关于java中String类型参数传递的问题,谁能给我讲清楚

问题描述 关于java中String类型参数传递的问题,谁能给我讲清楚 我知道java里面参数传递是值传递,这点不用给我解释了.但是: import java.util.*; public class Test { public static void change(String s){ s="2"; } public static void main(String[] args) throws Exception{ String s="1"; change(s);

string-关于java中String的对象问题

问题描述 关于java中String的对象问题 String s = ""abc"";String s1 =""a""+""b""+""c"";看有关书籍介绍说这上面两句代码内存中创建了4个对象,但是也有些资料上说是只创建了一个对象,因为编译器会把字符串常量在编译过程中直接相加?还有就是 String a = ""abc&quo

字符串-java中String str1…的区别,详细见内容

问题描述 java中String str1-的区别,详细见内容 String str1,str2; str1 = "we are friends"; str2 = "we are friends"; 和 String str1 = "we are friends"; String str2 = "we are friends"; 在内存上的区别是什么? 是否有区别? 解决方案 没有区别,他们指向同一个对象 解决方案二: 就最后

java中string 类型的对象间比较的学习笔记

在JAVA 中String 有两种不同的赋值方式 ,"="和new一个新的对象,虽然在输出时显示的内容是一样的,但数据存储的原理是不同的. String a="ab"; String b="ab"; System.out.println(a==b);//true 三句话的执行时这样的:        当把"ab"赋值给a变量时,因为是用"="直接赋值,此时"ab"作为字面量存在栈中,然后