其實 null 這個東西,真是讓人又愛又恨,悲喜參半的東西。
用的好了,能表征很多狀態,并在程序中很好實現狀態的傳遞,用的不好了,各種NPE問題可以把你煩死……
好吧,以上是題外話,那么這次問題的主旨在于,鑒于null這種關鍵字很大程度上豐富了我們的語義,并且隨著JDK5之后裝箱和拆箱的自動進行,null的出場概率更是越來越多。
但是隨之而來一個問題就是,當我們想要打印一個null來表示該對象為null的時候,什么操作去處理才是安全可靠的呢?
本文的主旨主要就是對此做一個簡單的備忘,以防之后模棱兩可的時候又要去看很久,畢竟好記性不如爛筆頭嘛,那么首先先把備忘的表格寫上(希望大家補充和指教~ :D)
方法 | 結果 |
---|---|
println(objectNull) | 成功打印null |
String.valueOf(objectNull) | 成功打印null |
StringBuider.append(objectNull) | 成功打印null |
ObjectNull = null; ObjectNull .toString() | NPE |
"any" + objectNull | 成功打印null |
以上就是一個簡單的表格總結,那么為什么會產生這種原因呢?
具體原因分析
其實通過源碼來看,就會發現,其實問題都非常的清晰了,以下對每個操作類型都給出了實現的片段,來佐證實際程序中的操作結果。
println(objectNull)
可以發現,該函數內部對null做了特殊處理,如果是null對象則處理成一個“null”的字符串,從而不會造成NPE問題。
/**
* Prints a string. If the argument is <code>null</code> then the string
* <code>"null"</code> is printed. Otherwise, the string's characters are
* converted into bytes according to the platform's default character
* encoding, and these bytes are written in exactly the manner of the
* <code>{@link #write(int)}</code> method.
*
* @param s The <code>String</code> to be printed
*/
public void print(String s) {
if (s == null) {
s = "null";
}
write(s);
}
String.valueOf(objectNull)
/**
* Returns the string representation of the <code>Object</code> argument.
*
* @param obj an <code>Object</code>.
* @return if the argument is <code>null</code>, then a string equal to
* <code>"null"</code>; otherwise, the value of
* <code>obj.toString()</code> is returned.
* @see java.lang.Object#toString()
*/
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
StringBuider.append(objectNull) :
雖然StringBuilder的append方法有比較多的重載方法,但是總體來說,都對null對象做了特殊的處理,方式主要有兩種:1.調用String.valueOf方法(具體實現原理如上所示);2.對null的字符串對象直接調整為打印“null”
// (筆者注): part1
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
// (筆者注): part2
/**
* Appends the specified string to this character sequence.
* <p>
* The characters of the {@code String} argument are appended, in
* order, increasing the length of this sequence by the length of the
* argument. If {@code str} is {@code null}, then the four
* characters {@code "null"} are appended.
* <p>
* Let <i>n</i> be the length of this character sequence just prior to
* execution of the {@code append} method. Then the character at
* index <i>k</i> in the new character sequence is equal to the character
* at index <i>k</i> in the old character sequence, if <i>k</i> is less
* than <i>n</i>; otherwise, it is equal to the character at index
* <i>k-n</i> in the argument {@code str}.
*
* @param str a string.
* @return a reference to this object.
*/
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
ObjectNull = null; ObjectNull .toString() :
如果沒有主動的重寫Object的toString方法,則會調用Object(所有類的父類)中的toString方法,可見這種方法是沒有對null做特殊處理的,所以如果如Java中鼓勵的那樣,我們最好針對自己的對象重寫toString來避免這種NPE的問題。
其次,我們可以挑選裝箱類型來看一下,也會發現,因為有自動拆箱的過程存在,也會存在NPE問題,所以這種場景下也需要注意。
// object
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
// Integer
public String toString() {
return toString(value);
}
"any" + objectNull :
參考這篇文章《在java中,字符串的加法是如何實現的?》,可以發現實際上這種加號的實現原理其實就是基于StringBuilder的,也就很好理解為什么加號會有這種表現了。
小結
因此,使用toString的方式來打印null是存在問題的,而其它的幾種方式都得益于方法內部的兼容處理,可以正常的處理null的打印。
特別有意思的是,+號的處理,實際上是通過StringBuilder來實現的,不得不感慨一下這種復用思想的使用,結合java中string在堆中的實際處理情況來理解,感覺也是一個非常有意思的處理方式。