(Turn) Java String's fault-tolerant handling of null objects

The first question
directly print the null String object, what will be the result?
String s = null;
System.out.print(s);
The result of the operation is that
null
does not throw an exception as the book says, but prints null. Apparently the clue to the problem lies in the source code of the print function. We found the source code of print:
public void print(String s) {
    if (s == null) {
        s = "null";
    }
    write(s);
} When
I saw the source code, I realized that it was just a judgment, simple Rough, maybe you're a little disappointed with the simple implementation of the JDK. Rest assured, the first question is just the appetizers, the big meal is yet to come.
The second problem is to
print a null non-String object, such as Integer:
Integer i = null;
System.out.print(i);
The result of the operation is not unexpected:
null
Let's look at the source code of print:
public void print (Object obj) {
    write(String.valueOf(obj));
}
A little different, it seems that the secret is hidden in valueOf.
public static String valueOf(Object obj) {
    return (obj == null) ? "null" : obj.toString();
}
Seeing this, we finally discovered the secret that printing a null object will not throw an exception. The print method handles String objects and non-String objects separately.
String object: directly determine whether it is null, if it is null, assign the value of "null" to the null object.
Non-String object: By calling the String.valueOf method, if it is a null object, it returns "null", otherwise it calls the object's toString method.
Through the above processing, it can be guaranteed that printing a null object will not go wrong.
At this point, this article should end.
What? How about a good meal? It's not enough to plug the gap between the teeth.
Just kidding. Let us now discuss the third question.
The third question (hidden meal)
What will be the result of concatenating a null object with a string?
String s = null;
s = s + "!";
System.out.print(s);
The result may be as you guessed:
null!
why? Tracking the code to run can find that this time has nothing to do with print. But the above code calls the print function, who would it be? + is the most suspect, but + is not a function, how can we see its source code? In this case, the only explanation is that the compiler has moved its hands and feet, and Skynet is full of sparse and omissions. The source code cannot be found. We can look at the bytecode generated by the compiler.
L0
LINENUMBER 27 L0
ACONST_NULL
ASTORE 1
L1
LINENUMBER 28 L1
NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
ALOAD 1
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava /lang/StringBuilder;
LDC "!"
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
ASTORE 1
L2
LINENUMBER 29 L2
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ALOAD 1
INVOKEVIRTUAL java/io/PrintStream.print (Ljava/lang/String;)V
Is the above bytecode confused? Here we are going to open the topic and come to the principle of Kankan + string splicing.
The compiler will optimize the addition of strings, first instantiate a StringBuilder, then append the added strings in order, and finally call toString to return a String object. If you don't believe me, see if StringBuilder appears in the bytecode above. For a detailed explanation, refer to this article Java Details: String Concatenation.
String s = "a" + "b";
//equivalent to
StringBuilder sb = new StringBuilder();
sb.append("a");
sb.append("b");
String s = sb.toString() ;
Back to our problem, now we know the secret is in the source code of the StringBuilder.append function.
//For String objects
public AbstractStringBuilder append(String str) {
    if (str == null)
        return appendNull();
    int len = str.length();
    ensureCapacityInternal(count + len);
    str.getChars(0, len, value, count);
    count += len;
    return this;
}
//针对非 String 对象
public AbstractStringBuilder append(Object obj) {
    return append(String.valueOf(obj));
}

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;
}
Now we suddenly realize that if the append function judges that the object is null, it will call appendNull and fill in "null".

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326570906&siteId=291194637