最近看到了一道題目:
正確答案:C
下面就這題的其他選項給出解析:
String
String不可變!先來看看String的源碼:
public final class String implements Serializable, Comparable<String>, CharSequence {
private final byte[] value;
private final byte coder;
private int hash;
private static final long serialVersionUID = -6849794470754667710L;
.......}
1、String由final修飾,說明final不能被繼承,不能被修改
2、String用來存儲數據的是字節數組 byte[] ,同樣也是由final修飾的。
3、雖然字節數組value是由final修飾,但是我們要清楚一個原則就是:String是引用型變量,要清楚在String的數組是放在堆中的,然後將棧中放的是數組在堆中的引用地址,而我們通常所用的就是一個指向堆中真實數組數據的一個引用地址,所以也稱String爲引用型對象。簡而言之:引用地址不可變,但是地址指向的堆中的數組數據是可以改變的。一旦創建了一個String對象,就在內存中申請一片固定的地址空間存放數據,不管怎麼變,地址是不會變的,但是地址所指向的空間真實存放的數據是可以改變的。再通俗點就是:你家的門牌號不會變,但是你家要住幾口人是你說了算。
下面通過一個Demo來說明:
public class Main {
public static String strAppend(String str){
str += "String";
return str;
}
public static StringBuilder sbAppend(StringBuilder str){
return str.append("StringBuilder");
}
public static void testBuilder() {
String s1 = new String("ysy ");
String s2 = Main.strAppend(s1);
System.out.println("String不可改變");
System.out.println(s2.toString());
System.out.println(s1.toString());
System.out.println("StringBuilder可以改變:");
StringBuilder sb1 = new StringBuilder("string");
StringBuilder sb2 = Main.sbAppend(sb1);
System.out.println(sb2.toString());
System.out.println(sb1.toString());
}
public static void main(String[] args) {
testBuilder();
}
}
在這裏能看出來String追加字符串後能新的字符串後,原來的字符串並沒有發生改變。
但是StringBuilder修改後就改變了自身的值。
Sting不可變的意義何在:
- 字符串常量池的需要
- 允許String對象緩存HashCode
- 安全性
在這裏可以參考一位大佬的博客爲什麼String要設計成不可變的? 說的很詳細。
StringBuffer是線程安全
先來看下StringBuffer的源碼:
public synchronized StringBuffer append(String str) {
this.toStringCache = null;
super.append(str);
return this;
}
注意下里面的關鍵字synchronize,線程安全。也可以通過下面一個Demo來例證:
先創建多個線程,然後在每一個線程中對一個變量進行自增多次,最後結果一定的話就說明這個是加了線程鎖的,也就是線程安全的。
public class Main {
public static void main(String[] args) {
// testBuilder();
testBuffer();
}
public static void testBuffer(){
StringBuffer buffer = new StringBuffer();
for(int i = 0; i < 100; i++ ){
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j <1000; j++){
buffer.append("j");
}
}
}).start();
}
try {
Thread.sleep(100);
}catch (Exception e){
System.out.println(e.getMessage());
}
System.out.println("線程安全"+buffer.length());
}
}
在這裏創建了100個線程,每個線程中對StringBuffer的對象append的一個字符,最後整個字符串的長度一定爲100000,所以可以看出來StringBuffer的線程安全的。
StringBuilder線程不安全
同樣將這個方法跟StringBuffer進行對比,結果是不一定的,能夠看出StringBuilder是線程不安全的。
public static void testBuilder() {
StringBuilder builder = new StringBuilder();
for(int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for(int j = 0; j < 1000; j++) {
builder.append("a");
}
}
}).start();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print("線程不安全:"+builder.length());
}
String、StringBuffer、StringBuilder三者的速度
速度:String最慢,StringBuffer次之,StringBuilder最快。