String、StringBuilder和StringBuffer详解

版权声明:原创文章,转载请注明出处! https://blog.csdn.net/L_15156024189/article/details/82826789

以JDK1.8源码为例

一、源码

String:

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

StringBuilder:

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{
    ……
    @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }
}

StringBuffer:

 public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{
    ……
    @Override
    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }

}

从源码中我们能看到:

1、三者都使用了final修饰,不能被继承;

2、三者都实现了java.io.Serializable接口,均可序列化和反序列化;

3、三者均实现同一接口CharSequence;

4、StringBuffer和StringBuilder都继承自同一个抽象类AbstractStringBuilder;

5、StringBuffer大部分方法前使用了synchronized关键字,而StringBuilder并没有使用。

二、String

1、String是不可变对象

首先,String是类,并不属于基本数据类型,虽然在使用中,有些地方和基本数据类型相似,例如:

String str="a";
int i=0;

String可以像基本数据类型一样赋值。但是它本质上是类,例如:

String s= new String("a");

其次,字符串一旦创建,对象永远无法改变,但字符串引用可以重新赋值,例如:

String str = "a";
str = str + "b";

代码中字符串"a"一旦创建用法无法改变,但是字符串引用str可以重新赋值为str+"b",也就是字符串"ab",事实上,在这个过程中总共创建了三个字符串对象,分别是:"a","b","ab"。

2、String常量池

java为提高字符串使用性能,静态字符串(字面量/常量/常量连接的结果)在字符串常量池中创建,并尽量使用同一个对象,重用静态字符串,对重复出现的字符串直接量,JVM会首先在常量池中查找,如果存在即返回该对象。例如:

//在字符串常量池中创建了"hello"对象,然后str1引用该对象
String str1="hello";
//不会创建新的"hello"对象,直接使用常量池中已有的"hello"对象
String str2="hello";
//字符串连接结果也是"hello",不会再创建"hello",直接使用常量池中"hello"
//但是注意的是,会创建"he"和"llo"对象
String str3="he"+"llo";
//输出true
System.out.println(str1==str2);
//输出true
System.out.println(str2==str3);

结果输出结果均为true,也就是str1、str2和str3都是相等的。

3、String内存编码及长度

Strign在内存中采用Unicode编码,任何一个字符(无论是中文还是英文)都算一个字符长度,占用两个字节。例如:

String str1="he";
String str2="你好";
//输出2
System.out.println(str1.length());
//输出2
System.out.println(str2.length());

三、StringBuilder和StringBuffer

1、StringBuilder和StringBuffer是可变对象

StringBuilder builder = new StringBuilder("a");
//输出a
System.out.println(builder);
builder.append("b");
//输出ab
System.out.println(builder);

2、StringBuilder的方法返回值

StringBuilder的很多方法的返回值也是StringBuilder,所以可以对一些操作连用,例如:

StringBuilder builder = new StringBuilder();
builder.append("a").append(1).append('c').deleteCharAt(2);
//输出a1
System.out.println(builder);

如果对字符串有大量的计算,应当选择StringBuilder,String在字符串运算中速度较慢。

3、StringBuffer与StringBuilder

两者基本相同,主要在线程操作中不同。StringBuffer因为使用了synchronized关键字,是线程安全的,而StringBuilder未使用synchronized关键字,是非线程安全的。例如:

package com.leboop;

public class StringTest {
	public static void main(String[] args) {
        StringBuilder builder = new StringBuilder();
        StringBuffer buffer = new StringBuffer();
        //循环创建1000个线程
        for(int i=0;i<1000;i++){
        	new Thread(new Runnable() {
				@Override
				public void run() {
					// TODO Auto-generated method stub
					//每个线程都要追加100个1
					for(int j=0;j<100;j++){
						builder.append("1");
						buffer.append("1");
						System.out.println(builder.length()+"-"+buffer.length());
					}
				}
			}).start();
        }
	}	
}

输出结果

99988-99992
99989-99993
99990-99994
99991-99995
99992-99996
99993-99997
99994-99998
99995-99999
99996-100000

正确结果应该是100000。显然StringBuilder非线程安全,如果要深入理解需要理解并发编程和jvm内存模型。所以在实际应用中,少量的单线程字符串操作使用String,大量的单线程字符串操作使用StringBuilder,多线程字符串操作使用StringBuffer。

猜你喜欢

转载自blog.csdn.net/L_15156024189/article/details/82826789
今日推荐