請先移步這篇文章,對String的原理講的很透徹:

cnblogs.com/zhangyinhua

接下來的主要是一些使用上的總結:

String的valueOf()根據不同的入參,會有不同的結果,這是典型的重載,由於實在是太多了,所以接下來我會選擇性的選擇部分進行介紹,讓大家少踩坑。

1.入參是Object,先上源碼:

public static String valueOf(Object obj) {

return (obj == null) ? "null" : obj.toString();

}

在賦值的時候會判斷是否為null,若是,則會返回"null"字元串,若否,則會調用父類Object的toString方法(這個就不重點關注了)

接下來我們來看個具體案例:

String test = null;

test = test + "age";

System.out.println(test);

輸出如下:

nullage

2. 入參為boolean,源碼如下:

public static String valueOf(boolean b) {

return b ? "true" : "false";

}

boolean型的數據是直接根據true與false,使用三木運算符判斷,直接返回相應字元串,這個也是我們平時不太會關注的地方

案例如下:

boolean boolTrue = true;

String boolStr = String.valueOf(boolTrue) + " === ";

System.out.println("boolean -- " + boolStr);

輸出如下:

boolean -- true ===

上述兩點是在文章的基礎上在進行補充的,接下來我們再來瞭解下字元常量池。再上面的文章中已經有介紹了,我想通過一些實例來驗證下:

String a = "abc";

String b = "abc";

String c = new String("abc");

// true

System.out.println(a==b);

// true

System.out.println(a.equals(b));

// false

System.out.println(a==c);

// true

System.out.println(a.equals(c));

看了上面的文章我們知道new String()實際上是在堆內存中開闢了兩塊空間,先產生了一個匿名對象"abc",之後new 的時候在產生一個對象"abc",並且c指向後產生的這個匿名對象的內存地址,之前的"abc",沒有任何對象指向這塊地址,變成了垃圾。所以在使用==進行比較的時候,兩個的地址不一致,等式不成立。但是這個時候如果我們想讓 a == c,這個操作成立的話,我們需要手動將new String()這個對象壓入到字元串常量池中,直接使用new String().intern()操作即可。代碼如下:

String str = "abc";

String newStr = new String("abc").intern();

System.out.println("手動入池之後, -- " + (str == newStr));

輸出結果如下:

手動入池之後, -- true

PS:因為JDK8新增了一個String.join方法,這個方法是用來拼接字元串的,源碼以及注釋中demo如下:

// demo message returned is: "Java-is-cool"

String message = String.join("-", "Java", "is", "cool");

public static String join(CharSequence delimiter, CharSequence... elements) {

Objects.requireNonNull(delimiter);

Objects.requireNonNull(elements);

// Number of elements not likely worth Arrays.stream overhead.

StringJoiner joiner = new StringJoiner(delimiter);

for (CharSequence cs: elements) {

joiner.add(cs);

}

return joiner.toString();

}

上面的源碼還不大看的明白,具體意思是啥,我們在看看add這個的源碼:

public StringJoiner add(CharSequence newElement) {

prepareBuilder().append(newElement);

return this;

}

這個prepareBuilder()是啥呢,再看看。

private StringBuilder prepareBuilder() {

if (value != null) {

value.append(delimiter);

} else {

value = new StringBuilder().append(prefix);

}

return value;

}

原來是在使用add方法的時候,會先append這個分割符,在append單個字元串,可以看到這個底層是使用StringBuilder方法來實現的。

這個時候我們在追查下StringBuilder的源碼,看看有沒有什麼需要注意的:

@Override

public StringBuilder append(CharSequence s) {

super.append(s);

return this;

}

繼承父類的append方法,在追蹤下:

@Override

public AbstractStringBuilder append(CharSequence s) {

if (s == null)

return appendNull();

if (s instanceof String)

return this.append((String)s);

if (s instanceof AbstractStringBuilder)

return this.append((AbstractStringBuilder)s);

return this.append(s, 0, s.length());

}

上面的appendNull()方法非常關鍵,在AbstractStringBuilder方法中所有append方法重載第一行都是這個判斷。我們再來看看appendNull方法實現代碼:

private AbstractStringBuilder appendNull() {

int c = count;

// 這個方法就不深究了,不用管它

ensureCapacityInternal(c + 4);

final char[] value = this.value;

value[c++] = n;

value[c++] = u;

value[c++] = l;

value[c++] = l;

count = c;

return this;

}

從源碼可以看到,他會直接添加null幾個字元,這個和String.valueOf(Object obj)方法是一致的判斷。所以通過這一長串的源碼深究,我們知道了這個方法的使用方式以及注意事項,接下來我們再和StringBuilder比較下效率如何:

final long time1 = System.currentTimeMillis();

for (int i = 0; i < 10000; i++) {

String aa = String.join("","bb","cc","dd","ee");

}

final long time2 = System.currentTimeMillis();

System.out.println("111 --- " + (time2 - time1));

for (int i = 0; i < 10000; i++) {

StringBuilder sb = new StringBuilder();

sb.append("bb")

.append("cc")

.append("dd")

.append("ee");

String aa = sb.toString();

}

System.out.println("222 --- " (System.currentTimeMillis() - time2));

輸入如下:

111 --- 22

222 --- 8

雖然String.join底層也是使用StringBuilder來實現的,但是還是直接使用StringBuilder的效率最高


推薦閱讀:
相關文章