Home >Java >javaTutorial >Summary of Java String's fault-tolerant handling of null objects

Summary of Java String's fault-tolerant handling of null objects

黄舟
黄舟Original
2017-03-31 11:07:061589browse

Preface

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.

Problem-solving process

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.

First question

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.

Second question

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. printThe method processes String objects and non-String objects separately.

  1. String object : Directly determine whether it is null. If it is null, assign the value "null" to the null object.

  2. 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.

Through the above processing, it can be guaranteed that printing null objects will not cause errors.

Here, this article should end.

What? Where's the promised dinner? It's not enough to fit between teeth.
Just kidding. Let’s explore the third question below.

The third question (hidden feast)

null What will be the result of concatenating objects and strings?

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

You may have guessed the result:

null!

Why? Tracking the code execution shows that this time it has nothing to do with

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

Are you confused after reading the above bytecode? Here we are going to change the topic and talk about the principle of

+ string splicing.

The compiler will optimize string addition. First instantiate a

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();

Back to our problem, now we know that the secret is in the source code of the

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++] = &#39;n&#39;;
    value[c++] = &#39;u&#39;;
    value[c++] = &#39;l&#39;;
    value[c++] = &#39;l&#39;;
    count = c;
    return this;
}

Now we suddenly realize that if the

append function determines that the object is null, it will call appendNull and fill "null".

Summary

We discussed three issues above, which lead to the fault-tolerant handling of String null objects in Java. The above example does not cover all processing situations and is considered to be an introduction.

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

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!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn