I was reading "Thinking in Java" recently and 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 main idea is: the native type will be automatically initialized to 0, but the object reference will be initialized to null. If you try to call the method of the object, a null pointer exception will be thrown. Normally, you can print a null object without throwing an exception.
I believe everyone will understand the first sentence easily. This is the basic knowledge of type initialization, but the second sentence makes me very confused: Why does printing a null object not throw an exception? With this question in mind, I began my journey of understanding. Below I will elaborate on my ideas for solving this problem, and dig into the JDK source code to find the answer to the problem.
It can be found that there are actually several situations for this problem, so we will discuss various situations in categories to see if we can get the answer in the end.
First of all, we break this problem down into three small problems and solve them one by one.
What result will you get if you directly print a null String object?
String s = null; System.out.print(s);
The result of the operation is
null
As expected, no exception was thrown as the book said, but null
was printed. Obviously 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. It is simple and crude. Maybe you are a little disappointed with the simple implementation of JDK. Don’t worry, the first question is just an appetizer, the meal is yet to come.
Print a null non-String object, for example, Integer:
Integer i = null; System.out.print(i);
The running result is not surprising:
null
We Let’s take a look at the source code of print
:
public void print(Object obj) { write(String.valueOf(obj)); }
is 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 of printing null objects without throwing exceptions. print
The method processes String objects and non-String objects separately.
String object : Directly determine whether it is null. If it is null, assign the value "null"
to the null object.
Non-String object: By calling the String.valueOf
method, if it is a null object, "null"## will be returned #, otherwise call the object's
toString method.
What? Where's the promised dinner? It's not enough to fit between teeth.
Just kidding. Let’s explore the third question below.
String s = null; s = s + "!"; System.out.print(s);
null!
print. But the above code calls the
print function, who else could it be?
+ is the most suspicious, but
+ is not a function. How can we see its source code? In this case, the only explanation is that the compiler has tampered with it. The source code cannot be found. We can 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.<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
+ string splicing.
StringBuilder, then
append the added strings in order, and finally call
toStringReturns a
String object. If you don’t believe me, take a look at the bytecode above to see if
StringBuilder appears. For a detailed explanation, please refer to this article Java Details: String Splicing.
String s = "a" + "b"; //等价于 StringBuilder sb = new StringBuilder(); sb.append("a"); sb.append("b"); String s = sb.toString();
StringBuilder.append function.
//针对 String 对象 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; }
append function determines that the object is null, it will call
appendNull and fill
"null".
The above is the detailed content of Summary of Java String's fault-tolerant handling of null objects. For more information, please follow other related articles on the PHP Chinese website!