在 Thinking in Java 看到这样一段话:

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.

大意是:原生类型会被自动初始化为 0,但是对象引用会被初始化为 null,如果你尝试调用该对象的方法,就会抛出空指针异常。通常,你可以打印一个 null 对象而不会抛出异常。

第一句相信大家都会容易理解,这是类型初始化的基础知识,但是第二句就让我很疑惑:为什么打印一个 null 对象不会抛出异常?带着这个疑问,我开始了解惑之旅。下面我将详细阐述我解决这个问题的思路,并且深入 JDK 源码找到问题的答案。

解决问题的过程

可以发现,其实这个问题有几种情况,所以我们分类讨论各种情况,看最后能不能得到答案。

首先,我们把这个问题分解为三个小问题,逐一解决。

第一个问题

直接打印 null 的 String 对象,会得到什么结果?

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

运行的结果是

null

果然如书上说的没有抛出异常,而是打印了null。显然问题的线索在于print函数的源码中。我们找到print的源码:

public void print(String s) {if (s == null) {s = "null";}write(s);
}

看到源码才发现原来就只是加了一句判断而已,简单粗暴,可能你对 JDK 的简单实现有点失望了。放心,第一个问题只是开胃菜而已,大餐还在后面。

第二个问题

打印一个 null 的非 String 对象,例如说 Integer:

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

运行的结果不出意料:

null

我们再去看看print的源码:

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

有点不一样的了,看来秘密藏在valueOf里面。

public static String valueOf(Object obj) {return (obj == null) ? "null" : obj.toString();
}

看到这里,我们终于发现了打印 null 对象不会抛出异常的秘密。print方法对 String 对象和非 String 对象分开进行处理。

String 对象:直接判断是否为 null,如果为 null 给 null 对象赋值为”null”。

非 String 对象:通过调用String.valueOf方法,如果是 null 对象,就返回”null”,否则调用对象的toString方法。

通过上面的处理,可以保证打印 null 对象不会出错。

到这里,本文就应该结束了。
什么?说好的大餐呢?上面还不够塞牙缝呢。
开玩笑啦。下面我们来探讨第三个问题。

第三个问题(隐藏的大餐)

null 对象与字符串拼接会得到什么结果?

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

结果可能你也猜到了:

null!

为什么呢?跟踪代码运行可以发现,这回跟print没有什么关系。但是上面的代码就调用了print函数,不是它会是谁呢?+的嫌疑最大,但是+又不是函数,我们怎么看到它的源代码?这种情况,唯一的解释就是编译器动了手脚,天网恢恢,疏而不漏,找不到源代码,我们可以去看看编译器生成的字节码。

L0LINENUMBER 27 L0ACONST_NULLASTORE 1
L1LINENUMBER 28 L1NEW java/lang/StringBuilderDUPINVOKESPECIAL java/lang/StringBuilder.<init> ()VALOAD 1INVOKEVIRTUAL 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
L2LINENUMBER 29 L2GETSTATIC java/lang/System.out : Ljava/io/PrintStream;ALOAD 1INVOKEVIRTUAL java/io/PrintStream.print (Ljava/lang/String;)V

看了上面的字节码是不是一头雾水?这里我们就要扯开话题,来侃侃+字符串拼接的原理了。

编译器对字符串相加会进行优化,首先实例化一个StringBuilder,然后把相加的字符串按顺序append,最后调用toString返回一个String对象。不信你们看看上面的字节码是不是出现了StringBuilder。详细的解释参考这篇文章 Java细节:字符串的拼接。

String s = "a" + "b";
//等价于
StringBuilder sb = new StringBuilder();
sb.append("a");
sb.append("b");
String s = sb.toString();

再回到我们的问题,现在我们知道秘密在StringBuilder.append函数的源码中。

//针对 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函数如果判断对象为 null,就会调用appendNull,填充”null”。

来源:http://blog.xiaohansong.com/2016/03/13/null-in-java-string/

Java 中关于 null 对象的容错处理相关推荐

  1. java static null,我们可以在Java中使用null对象调用静态方法吗?如果是这样,怎么样?...

    Since static methods can be called directly from the class (i.e. ClassName.methodName), why it is re ...

  2. Java中有关Null的9件事

    对于Java程序员来说,null是令人头痛的东西.时常会受到空指针异常(NPE)的骚扰.连Java的发明者都承认这是他的一项巨大失误.Java为什么要保留null呢?null出现有一段时间了,并且我认 ...

  3. java 返回空数组_避免在Java中检查Null语句

    1.概述 通常,在Java代码中处理null变量.引用和集合很棘手.它们不仅难以识别,而且处理起来也很复杂.事实上,在编译时无法识别处理null的任何错误,会导致运行时NullPointerExcep ...

  4. java中的null类型---有关null的9件事

    摘自 https://blog.csdn.net/qq_25077777/article/details/80174763 今天听到一个问题,java中的null类型,null竟然是一种类型 java ...

  5. java 字符串是对象吗_解析Java中的String对象的数据类型

    解析Java中的String对象的数据类型 2007-06-06 eNet&Ciweek 1. 首先String不属于8种基本数据类型,String是一个对象. 因为对象的默认值是null,所 ...

  6. (转)java中对集合对象list的几种循环访问总结

    Java集合的Stack.Queue.Map的遍历 在集合操作中,常常离不开对集合的遍历,对集合遍历一般来说一个foreach就搞定了,但是,对于Stack.Queue.Map类型的遍历,还是有一些讲 ...

  7. java 类 null_深入理解java中的null“类型”

    本文研究的主要是java中的null"类型"的相关实例,具体介绍如下. 先给出一道简单的null相关的题目,引发我们对null的探讨,后面会根据官方语言手册对null"类 ...

  8. java 删除二维数组中的null_避免在Java中检查Null语句

    1.概述 通常,在Java代码中处理null变量.引用和集合很棘手.它们不仅难以识别,而且处理起来也很复杂.事实上,在编译时无法识别处理null的任何错误,会导致运行时NullPointerExcep ...

  9. 在java中对null的理解

    转载:https://www.cnblogs.com/X-World/p/5686122.html Java中的Null是什么? 1)首先,null是关键字,像public.static.final. ...

最新文章

  1. [翻译] Ruby Golf
  2. AI大牛李飞飞最新去向, 加入Twitter 董事会
  3. leangoo新增自定义模板、移动卡片、复制泳道功能
  4. sharding jdbc根据年月分表
  5. Java虚拟机:常见JVM参数配置和GC性能优化
  6. ***CI新增记录成功后的返回值判断,是用isset还是empty
  7. 我用ABAP做过的那些无聊的事情
  8. sql 数据库 实例删除
  9. 定积分华里士公式推广_数学分析第九章《定积分》备考指南
  10. (73)FPGA面试题-Verilog实现5人表决器
  11. 通过RxJS理解响应式编程
  12. docker 定时重启脚本_群晖Docker容器定时执行重启任务
  13. 按键精灵打怪学习-窗口绑定技能
  14. App山寨疯狂 爱加密Apk加密平台防破解
  15. Goolge Chrome 浏览器下载不了文件的解决办法
  16. Python学习笔记(字符串、列表、字典)
  17. c语言文件大小限制,文件超过某个大小就删除(C语言)
  18. 提高php代码质量 36计
  19. 语音识别提取视频文案
  20. h5 默认为移动端页面_20条移动前端H5页面开发规范,h5开发必看文档

热门文章

  1. JAVA web项目报错no sigar-x86-winnt.dll in java.library.path
  2. IOSday05 UIScrollView使用
  3. oracle创建表空间脚本
  4. Dedicated and Shared Server
  5. 【机器学习】K-Means算法的原理流程、代码实现及优缺点
  6. Linux内核目录结构(2.6版本以上的kernel)
  7. AJAX-prototype.js实现Ajax
  8. 华为自带时钟天气下载_华为EMUI10的最大亮点是什么?
  9. qtmessagebox对话框里自定义按钮文本_Word里表格都是这么来的 — 生成绘制表格有技巧...
  10. HDU 6070 Dirt Ratio(线段树、二分)