# 一、String 的设计

String 是一个线程安全的类。我们可以看到在 String 中没有使用任何 “锁”,那么 String 是如何实现线程安全的?

# 1、String 被 final 修饰,这使得 String 无法被继承,并修改里面的方法

# 2、String 被设计成不可变对象

什么是不可变对象?

先来说说线程安全,之所以会存在线程安全的问题,是因为存在多个线程对同一个共享的资源对象进行“读写”操作,单纯只是读并不会有问题,问题出在“读写” 同时发生的情况。

而不可变对象的意思是,每一个线程操作的String 对象,对于当前线程来说都是唯一的,一旦有线程对某个 String 进行修改,那么生成会是一个新的 String 对象。

从代码的角度来看可能,来看几个简单的方法,concatreplace

public final class String {
	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) {
			......
			return new String(buf, true);
		}
		 return this;
	}
}

这样对当前字符串进行了修改,必定会生成一个新的对象,而不会再此之上进行修改,也就是说对于一个 String 对象,从它创建之后,就不会再进行变更了,所以 初始化之后的String 是一个不可变的对象。

同样的,我们通过简单的代码测试一下,

    public static void main(String[] args) {
        String a = "a";
        System.out.println("a:" + a);
        String b = a.concat("b");
        System.out.println("b:" + b);
        String c = b.replace("a","c");
        System.out.println("c:" + c);

        // 对比对象
        System.out.println("a 和 b是否为同一个对象:" + a == b);
        System.out.println("a 和 c是否为同一个对象:" + a == c);
        System.out.println("b 和 c是否为同一个对象:" + b == c);
    }

# 二、String 对比 StringBuffer 和 StringBuilder

# 线程安全问题上

String 被设计成为不可变对象,且String 无法被继承,所以本身就是线程安全的。

StringBuffer 针对所有方法都在方法加上了 synchronized 锁,以当前实例对象为锁保证线程安全问题

StringBuilder 是线程不安全的,所有的append 操作直接在同一个 char[] 数组中进行,多线程并发操作会有线程安全问题。

# 字符串拼接执行效率对比

# “+” 拼接的效率

public class StringDemo {
    public static void main(String[] args) {
        String s2 = "";
        s2 += "abc";
        System.out.println(s2);
    }
}

来看一下上述代码的字节码信息

所以如果简单对比 StringStringBuilder 能够知道,StringBuilder 的效率更高,因为 String 需要多生成一个对象,多一个对象的操作。

# StringBuilderStringBuffer 字符串拼接对比

StringBuilderStringBuffer在实现上,几乎差不多,差别就在于StringBuffer` 是线程安全的,所有操作都需要获取 synchroinzed 锁,

所以 StringBuffer 整体上来说会比 StringBuilder 慢一点

# 总结

总的来说,效率从高到低:StringBuilder > StringBuffer > String

String 每次拼接都需要多生成一个新的对象,相比较于:StringBuilder 和 StringBuffer 来说效率都低。

而 StringBuffer 为了线程安全,加了 synchronized 同步锁,所以效率会比 StringBuilder 低一点。

不过总的来说,在无法确定线程安全问题情况下,直接使用 StringBuffer 是最安全,效率较高的选择。

# 三、String 的几道面试题

# 1、以下语句的输出结果

String s1="abc";
String s2="abc";
System.out.println(s1==s2);
System.out.println(s1.equals(s2));

#### 结果
true
true

在 Java 中存在常量池,常量池中的数据只有一份。 虽然 s1 和 s2 指向的是同一个常量对象,但是他们仍然是线程安全,因为该值只有一份。 如果此时有操作对 s1 进行修改,那么 s1 所指向的对象也会随着变更,所以 s1 ,s2 仍然是不可变的对象。

    public static void main(String[] args) {
        String s1 = "abc";
        String s2 = "abc";
        System.out.println("s1 == s2:" + ( s1 == s2 ));
        System.out.println("s1 equal s2:" + ( s1.equals(s2)) );
        // 对 s1 进行写操作
        System.out.println("=============================================");
        s1 = s1 + s1;
        System.out.println("s1 新值:" + s1);
        System.out.println("s1 == s2:");
        System.out.println("s1 == s2:" + ( s1 == s2 ));
        System.out.println("s1 equal s2:" + ( s1.equals(s2)) );
    }

# 2、 String s1=new String(“abc”)语句创建了几个对象?

public static void main(String[] args) {
        String s1 = new String("abc");
        String s2 = "abc";
        // 地址不一样,但是值一样,一个在常量池一个在堆中,所以有两个对象
        System.out.println("s1 == s2:" + ( s1 == s2 ));
        System.out.println("s1 equal s2:" + ( s1.equals(s2)) );
}

创建了两个对象,一个在常量池中,一个在堆中,所以应该避免使用 new String()

# 3、对比一下两个代码块

public static void main(String[] args) {
        method1();
        method2();
    }
    public static void method1(){
        String s1 = new String("abc"); // 堆中
        String s2 = "abc"; // 常量池中
        // 地址不一样,但是值一样,一个在常量池一个在堆中,所以有两个对象
        System.out.println("s1 == s2:" + ( s1 == s2 ));
        System.out.println("s1 equal s2:" + ( s1.equals(s2)) );
    }
    public static void method2(){
        String s1 = "a" + "b" + "c"; // 常量池中
        String s2 = "abc"; // 常量池中
        // 编译过程中,由于常量化机制,s1成为了 "abc" 所以也在常量池中创建
        System.out.println("s1 == s2:" + ( s1 == s2 ));
        System.out.println("s1 equal s2:" + ( s1.equals(s2)) );
    }
最近更新时间: 7/12/2020, 9:24:26 PM