您的位置:首页 > 产品设计 > UI/UE

String,StringBuffer和StringBuilder这三个好基友

2017-03-07 16:23 323 查看
虽然在项目开发中,需要经常用上String处理字符串,但一直对它的理解还停留在如何使用的阶段,所以趁着这段时间复习一下,也写下博文希望把知识记得更深刻.

  

如有不正之处,请多多谅解和指正,谢谢.

1.创建String内存空间

创建String对象基本有两种方法

String 变量名 = value

通过new实例化一个String对象

我们看以下例子

public static void main(String[] args) {
String s1 = "Hello World"; //第一种创建方法
String s2 = "Hello World";
String s3 = new String("Hello World"); //第二种创建方法

//String中的 == 是比较他们是否指向同一个内存空间
System.out.println(s1 == s2);
//运行结果为ture
System.out.println(s1 == s3);
//运行结果为false
}


  从这里看出s1和s2是分配到同一个内存空间里,而s3是另外一个内存空间,这两种方法虽然得出值都是Hello World但是他们的处理是大大不同的



如上图所示

  其实当s1即将要被赋值的时候Jvm会看内存里是否存在Hello World这个值的内存空间,如果没有就直接创建该值的内存空间,将s1指向到这个内存空间当中

  然后s2要被赋值时Jvm已经查到存储Hello World这个值的内存空间,所以也就直接将s2指向到这个内存空间当中

  s3比较特别因为是它是通过new出来的一个实例,Jvm不会管内存里面是否已经存在Hello World这个内存空间,而是重新开辟一个空间让s3指向过去

所以最终得出这个结果.

2.String是一个不可变类

当学习一个类最好的方法就是查看该类源码

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {

private final char value[];

private int hash; // Default to 0

private static final long serialVersionUID = -6849794470754667710L;

private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];

public String() {
this.value = new char[0];
}

......

}


  从中我们可以看到String不管是类还是属性都被定义成final,所以每当我们对String对象进行从新赋值时,实际上就是改变内存空间的指向,而不是在原内存空间进行值的改变

很简单的道理,之前例子s1 和 s2 他们指向也是指向同一个内存空间,我改变了s2的值,因为s2指向的内存空间发生了变化,s1也会随这个变化而变化,最终导致我只想该s2结果也把s1的值也一起改了

这就是把String定义为final的原因

3.String,StringBuffer,StringBuilder的区别

因为String是不可变类,无法对字符串进行直接的修改

当做大量的字符串处理时通过String会消耗很多资源,一般我们做大量字符处理时,都会用上StringBuffer,StringBuilder这两个类

public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence {

......

public synchronized StringBuffer append(String str) {
super.append(str);
return this;
}
}


public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence {

......

public StringBuilder append(String str) {
super.append(str);
return this;
}
}


从源码中我们可以看到,不管在继承的类还是实现的接口都是一样的,唯一的不同是StringBuffer的append方法是带有synchronized,也就是说StringBuffer是线程安全的,而StringBuilder是线程非安全,因为String定义是final的关系,所以String天生就是线程安全的.

我们看下他们的执行效率

package com.rwj.test;

public class StringDemo {

public static void main(String[] args) {
string();
stringBuffer();
stringBuilder();
}

private static void string(){
long startTime = System.currentTimeMillis();
String s = new String();
for(int i = 0; i <= 100000; i++){
s += i;
}
long endTime = System.currentTimeMillis();
System.out.println("String: " + (endTime - startTime) + "毫秒.");
}

private static void stringBuffer(){
long startTime = System.currentTimeMillis();
//线程安全
StringBuffer s = new StringBuffer();
for(int i = 0; i <= 100000; i++){
s.append(i);
}
long endTime = System.currentTimeMillis();
System.out.println("StringBuffer: " + (endTime - startTime) + "毫秒.");
}

private static void stringBuilder(){
long startTime = System.currentTimeMillis();
//非线程安全
StringBuilder s = new StringBuilder();
for(int i = 0; i <= 100000; i++){
s.append(i);
}
long endTime = System.currentTimeMillis();
System.out.println("StringBuilder: " + (endTime - startTime) + "毫秒.");
}

}


运行结果为



所以需要做到大量字符时

在不考虑线程的情况下优先StringBuilder,如果加大循环次数效果尤其明显

考虑线程的情况则使用StringBuffer但运行效率也是降低
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: