Fault tolerance of String to null object in Java!

Author: Xiao Hansong blog.xiaohansong.com/2016/03/13/null-in-java-string/

Recently, I was reading Thinking in Java, and I saw this passage:

_Primitives that are fields in a class are automatically initialized to zero, as noted in the Everything Is an Object chapter. _

_But the object references are initialized to null, and if you try to call methods for any of them, you'll get an exception-a runtime error. _

Conveniently, you can still print a null reference without throwing an exception.

The native type will be automatically initialized to 0, but the object reference will be initialized to null. If you try to call the object's method, a null pointer exception will be thrown. Usually, you can print a null object without throwing an exception.

The first sentence is easy for everyone to understand, which is the basic knowledge of type initialization, but the second sentence makes me wonder: why does printing a null object not throw an exception? With this question in mind, I embarked on a journey of perplexity. Next, I will elaborate on my ideas to solve this problem, and go deep into JDK source code to find the answer to the problem.

Problem solving process

It can be found that there are several situations in this question, so we will discuss them by category to see if we can get the answer at last.

First of all, we divide this problem into three small problems and solve them one by one.

First question

What is the result of printing a null String object directly?

String s = null;  
System.out.print(s);  

The result of the operation is

null  

As the book said, no exception was thrown, but null was printed. Obviously, the clue 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);  
}  

Only when you see the source code, you find that it's just a judgment. It's simple and rough. Maybe you are a little disappointed in the simple implementation of JDK. Don't worry. The first problem is just appetizers. The big meal is still in the back.

Second question

Print a null non String object, such as Integer:

Integer i = null;  
System.out.print(i);  

The results of the run were unexpected:

null  

Let's look at the source code of print:

public void print(Object obj) {     write(String.valueOf(obj)); }

It's 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 null objects does not throw exceptions. The print method treats String objects separately from non String objects.

String object: directly judge whether it is null. If it is null, assign "null" to the null object.

Non string object: by calling String.valueOf Method, return "null" if it is a null object, otherwise call the toString method of the object.

Through the above processing, you can ensure that printing null objects will not fail.

By this point, this article should be finished.
what? What about the agreed meal? There's not enough teeth on it.
I'm kidding. Let's discuss the third question.

Third question (hidden meal)

What is the result of splicing null object and string?

String s = null;  
s = s + "!";  
System.out.print(s);'  

As you might have guessed:

null!  

Why? Tracing the code running shows that it has nothing to do with print this time. But the above code calls the print function, not who it will be? +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. It's too big to find the source code. Let's take a 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. ()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  

Did you see the bytecode above? Here we are going to talk about the principle of string splicing.

The compiler will optimize the string addition, first instantiate a StringBuilder, then add the string in order append, and finally call toString to return a String object. I don't believe you can see if StringBuilder appears in the bytecode above. For a detailed explanation, refer to the Java details of this article: String splicing.

String s = "a" + "b";  

Equivalent to

StringBuilder sb = new StringBuilder();  
sb.append("a");  
sb.append("b");  
String s = sb.toString();  

Back to our question, now we know the secret is 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;  
}  

//For non String objects  
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 realize that if the append function judges that the object is null, it will call appendNull and fill in "null".

summary

We discussed three problems above, which leads to the fault tolerance of String to null object in Java. The above example doesn't cover all the processing situations. It's a good example.

How to make the null object in the program under our control is something we need to pay attention to when we program.

Recommend to my blog to read more:

1.Java JVM, collection, multithreading, new features series

2.Spring MVC, Spring Boot, Spring Cloud series tutorials

3.Maven, Git, Eclipse, Intellij IDEA series tools tutorial

4.Latest interview questions of Java, backend, architecture, Alibaba and other large factories

Feel good, don't forget to like + forward!

Tags: Programming Java Spring JDK jvm

Posted on Wed, 20 May 2020 00:41:26 -0400 by r2ks