Java 中的String、StringBuilder与StringBuffer的区别联系(转载)
1 String 基础
想要了解一个类,最好的办法就是看这个类的源代码,String类源代码如下:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[]; /** Cache the hash code for the string */
private int hash; // Default to 0 /** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L; /**
* Class String is special cased within the Serialization Stream Protocol.
*
* A String instance is written into an ObjectOutputStream according to
* <a href="{@docRoot}/../platform/serialization/spec/output.html">
* Object Serialization Specification, Section 6.2, "Stream Elements"</a>
*/
private static final ObjectStreamField[] serialPersistentFields =
new ObjectStreamField[0];
从上面代码可以看出:
① String类是final类,即意味着String类不能被继承,并且它的成员方法都默认为final方法。
② 上面列出了String类的成员属性,String类其实是通过char数组来保存字符串的。
再来看String类的一些方法:
public String substring(int beginIndex, int endIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
if (endIndex > value.length) {
throw new StringIndexOutOfBoundsException(endIndex);
}
int subLen = endIndex - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return ((beginIndex == 0) && (endIndex == value.length)) ? this
: new String(value, beginIndex, subLen);
} public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
public String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
int len = value.length;
int i = -1;
char[] val = value; /* avoid getfield opcode */ while (++i < len) {
if (val[i] == oldChar) {
break;
}
}
if (i < len) {
char buf[] = new char[len];
for (int j = 0; j < i; j++) {
buf[j] = val[j];
}
while (i < len) {
char c = val[i];
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
return new String(buf, true);
}
}
return this;
}
上面三个方法,无论是substring、concat、replace操作都不是在原来的基础上进行的,而是重新生成了一个新的字符串对象。也就是说进行这些操作后,最原始的字符串没有改变。
牢记: 对String对象的任何改变都不会影响到原对象,相关的任何change操作都会产生新的对象。
2 深入理解String、StringBuffer、StringBuilder
(1) String str = "Hello" 与 String str = new String("Hello") 的区别
String str1="Hello latiny";
String str2="Hello latiny";
String str3=new String("Hello latiny");
String str4=new String("Hello latiny"); System.out.println(str1==str2);
System.out.println(str1==str3);
System.out.println(str3==str4);
结果为:
true
false
false
为什么会出现这样的结果?这里我们作一个详细解释
① 首先得引入常量池的概念,常量池指的是在编译期就被确定,并保存在已编译的.class文件中的一些数据。它包含了类、方法、接口等的常量,也包含了字符串常量。
String str1="Hello latiny"
String str2="Hello latiny";
其中 Hello latiny 都是字符串常量。它们在编译时就被确定了,所以str1 == str2为true
② 用new String()创建的字符串不是常量,不能在编译时确定,所以new String() 创建的字符串不放入常量池中,它们有自己的地址空间。更进一步解释一下,通过new 关键字创建的对象是在堆区进行的,而在堆区进行对象的生成过程是不会去检测该对象是否已经存在。因此通过new创建的对象,一定是不同的对象,即使字符串内容相同。
(2) 既然在Java中已经存在了String类,为什么还需要StringBuffer与StringBuilder类呢?
看下面这段代码:
public class StringTest { public static void main(String[] args) { String str = "";
for(int i=0; i<10000; i++)
{
str+="Hello";
} } }
str+="Hello"; 这句代码的过程相当于将原有的 str 变量指向的对象内容取出与 Hello 作字符串相加操作,再存进另一个新的String 对象中,再让str变量的指向新生成的对象。反编译其字节码文件就知道了:
C:\Work\Project\Java\Eclipse\JustTest\bin\com\latiny\string>javap -c StringTest
警告: 二进制文件StringTest包含com.latiny.string.StringTest
Compiled from "StringTest.java"
public class com.latiny.string.StringTest {
public com.latiny.string.StringTest();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return public static void main(java.lang.String[]);
Code:
0: ldc #16 // String
2: astore_1
3: iconst_0
4: istore_2
5: goto 31
8: new #18 // class java/lang/StringBuilder
11: dup
12: aload_1
13: invokestatic #20 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
16: invokespecial #26 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
19: ldc #29 // String Hello
21: invokevirtual #31 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: invokevirtual #35 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
27: astore_1
28: iinc 2, 1
31: iload_2
32: sipush 10000
35: if_icmplt 8
38: return
}
从这段反编译的字节码文件可以看出:从第8行到35行是整个循环执行过程, 并且每次循环都会new 出一个StringBuilder对象,然后进行append操作,最后通过toString 方法返回String对象。也就是说这个循环执行完毕new出了10000个对象, 这是非常大的资源浪费。从上面还可以看出:str+="Hello"; 自动会被JVM优化成:
StringBuilder str1 = new StringBuilder(str);
str1.append("Hello");
str = str1.toString();
再来看下面这段代码:
public class StringTest { public static void main(String[] args) { StringBuilder str = new StringBuilder();
for(int i=0; i<10000; i++)
{
str.append("Hello");
} } }
反编译字节码文件得到如下代码:
C:\Work\Project\Java\Eclipse\JustTest\bin\com\latiny\string>javap -c StringTest
警告: 二进制文件StringTest包含com.latiny.string.StringTest
Compiled from "StringTest.java"
public class com.latiny.string.StringTest {
public com.latiny.string.StringTest();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return public static void main(java.lang.String[]);
Code:
0: new #16 // class java/lang/StringBuilder
3: dup
4: invokespecial #18 // Method java/lang/StringBuilder."<init>":()V
7: astore_1
8: iconst_0
9: istore_2
10: goto 23
13: aload_1
14: ldc #19 // String Hello
16: invokevirtual #21 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: pop
20: iinc 2, 1
23: iload_2
24: sipush 10000
27: if_icmplt 13
30: return
}
上面代码可以看出,循环从13行到27行结束,并且new 操作只进行了一次,也就是说只产生了一个对象, append操作是在原有对象的基础上进行的。因此循环10000次之后,这段代码所占的资源比直接使用String定义的变量要小得多
那有人又要问了既然有了StringBuilder类,为什么还需要StringBuffer类,查看源代码就知道了,StringBuilder与StringBuffer类拥有的成员属性及方法基本相同,区别是StringBuffer类的成员方法多了一个关键字: synchronized,很明显这个关键字是在多线程访问时起到安全保护作用的,即StringBuffer是线程安全的。
StringBuilder的insert方法:
@Override
public StringBuilder insert(int offset, Object obj) {
super.insert(offset, obj);
return this;
}
StringBuffer的insert方法:
@Override
public synchronized StringBuffer insert(int index, char[] str, int offset,
int len)
{
toStringCache = null;
super.insert(index, str, offset, len);
return this;
}
3 三个类不同场景的性能测试
public class StringTest { private static final int TIMES = 50000; public static void main(String[] args) {
TestString1();
TestBuilder();
TestBuffer(); // 直接字符相加与间接字符相加对比
TestString2();
TestString3();
} public static void TestString1()
{
String str = "";
long begin = System.currentTimeMillis();
for(int i=0; i<TIMES; i++)
{
str += "Java";
}
long end = System.currentTimeMillis(); System.out.println("操作"+str.getClass().getName()+"类型需要的时间:"+(end-begin)+"毫秒");
} public static void TestBuilder()
{
StringBuilder str = new StringBuilder();
long begin = System.currentTimeMillis();
for(int i=0; i<TIMES; i++)
{
str.append("Java");
}
long end = System.currentTimeMillis(); System.out.println("操作"+str.getClass().getName()+"类型需要的时间:"+(end-begin)+"毫秒");
} public static void TestBuffer()
{
StringBuffer str = new StringBuffer();
long begin = System.currentTimeMillis();
for(int i=0; i<TIMES; i++)
{
str.append("Java");
}
long end = System.currentTimeMillis(); System.out.println("操作"+str.getClass().getName()+"类型需要的时间:"+(end-begin)+"毫秒");
} public static void TestString2()
{
String str = "";
long begin = System.currentTimeMillis();
for(int i=0; i<TIMES; i++)
{
str="I"+"Love"+"Java";
}
long end = System.currentTimeMillis(); System.out.println("字符串直接相加操作需要的时间:"+(end-begin)+"毫秒");
} public static void TestString3()
{
String str1 = "I";
String str2 = "Love";
String str3 = "Java";
String str = "";
long begin = System.currentTimeMillis();
for(int i=0; i<TIMES; i++)
{
str = str1+str2+str3;
}
long end = System.currentTimeMillis(); System.out.println("字符串间接相加操作需要的时间:"+(end-begin)+"毫秒");
} }
测试环境:win7 + Eclipse + JDK1.8
结果为:
操作java.lang.String类型需要的时间:5395毫秒
操作java.lang.StringBuilder类型需要的时间:1毫秒
操作java.lang.StringBuffer类型需要的时间:2毫秒
字符串直接相加操作需要的时间:1毫秒
字符串间接相加操作需要的时间:4毫秒
请按任意键继续. . .
上面提到JVM会自动优化 str+="Hello",看如下代码:
public static void TestString1()
{
String str = "";
long begin = System.currentTimeMillis();
for(int i=0; i<time; i++)
{
str+="Java";
}
long end = System.currentTimeMillis(); System.out.println("操作"+str.getClass().getName()+"类型需要的时间:"+(end-begin)+"毫秒");
} public static void TestString1Optimal()
{
String str = "";
long begin = System.currentTimeMillis();
for(int i=0; i<time; i++)
{
StringBuilder str1 = new StringBuilder(str); str1.append("Java"); str = str1.toString();
}
long end = System.currentTimeMillis(); System.out.println("模拟JVM优化操作需要的时间:"+(end-begin)+"毫秒");
}
执行结果:
操作java.lang.String类型需要的时间:11692毫秒
模拟JVM优化操作需要的时间:9958毫秒
得到验证。
对执行结果进行一般的解释:
① 对于直接相加字符串,效率很高,因为在编译器便确定了它的值,也就是说形如 "I"+"like"+"Java" 的字符串相加,在编译时被优化成 "Ilikejava"。对于间接相加(即包含字符串引用),刑如:str=str1+str2+str3 ,效率比直接相加低,因为在编译时编译器不会对引用变量进行优化。
② 三者的效率:
StringBuilder > StringBuffer > String
但是这只是相对的,如String str = "Hello" + "Latiny"; 比 StringBuilder str = new StringBuilder().append("Hello").append("Latiny"); 要高。
这三个类各有利弊,根据不同的情况选择使用:
当字符串相加操作或者改动较少时,使用String类;
当字符串相加操作较多时,使用StringBuilder吗,如果采用多线程,需要考虑线程安全则使用StringBuffer;
4 常见的String、 StringBuilder、StringBuffer面试题
(1) String a = "Hello2"; final String b = "Hello"; String c = b+"2"; System.out.println(a==c); 输出结果为true
(2) 下面代码输出结果为false:
public static void main(String[] args) {
// TODO Auto-generated method stub
//TestString1();
//TestString1Optimal();
String a = "Hello2";
final String b = getHello();
String c = b+"2"; System.out.println(a==c);
} public static String getHello()
{
return "Hello";
}
(3) 下面代码输出为true:
public static void main(String[] args) {
// TODO Auto-generated method stub
//TestString1();
//TestString1Optimal();
String a = "Hello2";
String b = a.intern();
System.out.println(a==b);
}
(4) 代码 I 与 II 的区别
String str = "I";
str += "like" + "Java"; //I
//str = str+"like" + "Java"; //II
① I的效率比II 高,I的 "like" + "Java" 在编译时会被优化为 likejava 而II 的不会
I 的反编译字节码
public class com.latiny.string.StringTest1 {
public com.latiny.string.StringTest1();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return public static void main(java.lang.String[]);
Code:
0: ldc #16 // String I
2: astore_1
3: new #18 // class java/lang/StringBuilder
6: dup
7: aload_1
8: invokestatic #20 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
11: invokespecial #26 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
14: ldc #29 // String likeJava
16: invokevirtual #31 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #35 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: astore_1
23: return
}
II 的反编译字节码
public class com.latiny.string.StringTest1 {
public com.latiny.string.StringTest1();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return public static void main(java.lang.String[]);
Code:
0: ldc #16 // String I
2: astore_1
3: new #18 // class java/lang/StringBuilder
6: dup
7: aload_1
8: invokestatic #20 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
11: invokespecial #26 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
14: ldc #29 // String like
16: invokevirtual #31 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: ldc #35 // String Java
21: invokevirtual #31 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: invokevirtual #37 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
27: astore_1
28: return
}
可以看出 I 中只进行了一次append操作,II 的进行了两次。
转自:http://www.cnblogs.com/dolphin0520/p/3778589.html
Java 中的String、StringBuilder与StringBuffer的区别联系(转载)的更多相关文章
- Java基础学习总结(65)——Java中的String,StringBuilder和StringBuffer比较
字符串,就是一系列字符的集合. Java里面提供了String,StringBuffer和StringBuilder三个类来封装字符串,其中StringBuilder类是到jdk 1.5才新增的.字符 ...
- String,StringBuilder与StringBuffer的区别
相信大家看到过很多比较String和StringBuffer区别的文章,也明白这两者的区别,然而自从Java 5.0发布以后,我们的比较列表上将多出一个对象了,这就是StringBuilder类.St ...
- String, StringBuilder 与StringBuffer的区别与联系
1.区别 (1)String构建的对象不能改变,每次对String进行操作时,如两个String相加,需要新建一个String对象,然后容纳最终的结果. 而StringBuilder与StringBu ...
- Java中的String,StringBuilder,StringBuffer三者的区别
最近在学习Java的时候,遇到了这样一个问题,就是String,StringBuilder以及StringBuffer这三个类之间有什么区别呢,自己从网上搜索了一些资料,有所了解了之后在这里整理一下, ...
- Java中的String,StringBuilder,StringBuffer三者的区别(转载)
最近在学习Java的时候,遇到了这样一个问题,就是String,StringBuilder以及StringBuffer这三个类之间有什么区别呢,自己从网上搜索了一些资料,有所了解了之后在这里整理一下, ...
- 转:Java中的String,StringBuilder,StringBuffer三者的区别
最近在学习Java的时候,遇到了这样一个问题,就是String,StringBuilder以及StringBuffer这三个类之间有什么区别呢,自己从网上搜索了一些资料,有所了解了之后在这里整理一下, ...
- [转载]Java中的String,StringBuilder,StringBuffer三者的区别
最近在学习Java的时候,遇到了这样一个问题,就是String,StringBuilder以及StringBuffer这三个类之间有什么区别呢,自己从网上搜索了一些资料,有所了解了之后在这里整理一下, ...
- Java中的String,StringBuilder,StringBuffer的区别
这三个类之间的区别主要是在两个方面,即运行速度和线程安全这两方面. 首先说运行速度,或者说是执行速度,在这方面运行速度快慢为:StringBuilder > StringBuffer > ...
- 【转】Java中的String,StringBuilder,StringBuffer三者的区别
https://www.cnblogs.com/su-feng/p/6659064.html 最近在学习Java的时候,遇到了这样一个问题,就是String,StringBuilder以及String ...
随机推荐
- Python取整函数
ceil() 向上取整 返回数字的上入整数,如ceil(4.1) 返回 5 ceil()接受的参数必须是数字类型,可以是True或者False,True(代表1),False(代表0),我试了ceil ...
- 一文读懂SpringCloud与Eureka,Feign,Ribbon,Hystrix,Zuul核心组件间的关系
概述 毫无疑问,Spring Cloud是目前微服务架构领域的翘楚,无数的书籍博客都在讲解这个技术.不过大多数讲解还停留在对Spring Cloud功能使用的层面,其底层的很多原理,很多人可能并不知晓 ...
- 注册mySQL到JDBC驱动程序方法浅谈
一.注册方法(4种) 1)服务提供者框架: 符合JDBC 4.0规范的驱动程序包含了一个文件META-INF/services/java.sql.Driver,在这个文件中提供了JDBC驱动实现的类名 ...
- (转)Spring Boot (十九):使用 Spring Boot Actuator 监控应用
http://www.ityouknow.com/springboot/2018/02/06/spring-boot-actuator.html 微服务的特点决定了功能模块的部署是分布式的,大部分功能 ...
- Django-rest-framework 接口实现 ModelSerializer 使用
ModelSerializer 使用 不需要单独写字段的 序列化工具类 相当于 ModelForm 写法:以及 注意事项: 继承 serializers.ModelSerializer 在类中填写 c ...
- linux学习笔记整理(六)
第七章 Centos7-文件权限管理本节所讲内容:7.1文件的基本权限:r w x (UGO)7.2文件的特殊权限:suid sgid sticky和文件扩展权限ACL7.3实战:创建一个让root都 ...
- SQLite 知识摘要 --- 线程模式、事务模式
本篇主要从SQLite事务执行的原理中寻找如何更高效地使用它. 本篇预备知识 我们先来了解下SQLite执行事务的基本流程,状态变化过程,再分析怎么使用才更优.SQLite定义的锁的状态有如下几种: ...
- 在Ubuntu18.04上使用Anaconda(python3.7)环境中安装tensorflow1.13.1
由于清华镜像源迟迟没有更新tensorflow1.13.1导致python3.7不能使用tensorflow 这里有一个解决方法 管理员模式打开(一定要管理员模式 不然会导致权限不足) 输入 pip ...
- 磁盘性能评价指标—IOPS和吞吐量
转:http://blog.csdn.net/hanchengxi/article/details/19089589 一.磁盘 I/O 的概念 I/O 的概念,从字义来理解就是输入输出.操作系统从上层 ...
- 路飞学城-Python开发集训-第5章
面向过程:核心是过程二字,过程是解决问题的步骤,相当于设计一条流水线,是机械式的思维方式 优点:复杂的问题流程化,进而简单化 缺点:可扩展性差 面向对象:核心是对象二字,对象就是特征与技能的结合体. ...