这个问题是我刷知乎看到的。

先给这个问题点个赞 —— 我们习以为常的东西突然被质问,有种“惊愕”+“反思”的感觉!

于是我稍微思考和研究了一下,以下是我的想法

NullPointerExeception 是什么?

程序员通常说的NullPointerExeception来自于Java https://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html

官方文档的描述是:

Thrown when an application attempts to use null in a case where an object is required. These include:
- Calling the instance method of a null object. 
- Accessing or modifying the field of a null object.
- Taking the length of null as if it were an array.
- Accessing or modifying the slots of null as if it were an array.
-Throwing null as if it were a Throwable value.
Applications should throw instances of this class to indicate other illegal uses of the null object. NullPointerException objects may be constructed by the virtual machine as if suppression were disabled and/or the stack trace was not writable.

翻译一下:

当Java程序试图对 null 做操作时就会抛出 NullPointerExeception ,例如:

  • 对null调用对象的实例方法
  • 对null访问或修改对象的域
  • 对null访问数组的length
  • 对null访问或修改某个数组位的元素
  • 手动抛出NullPointerExeception

后面的就不翻译了


简单而言,就是这样

Student s = fromSomewhere();
int age = a.age; // throw NullPointerExeception if a is null
s.doHomework(); // throw NullPointerExeception if a is null

或者

int[] arr = fromSomewhere2();
int length = arr.length; // throw NullPointerExeception if arr is null
arr[2] = 6; // throw NullPointerExeception if arr is null


我们原本以为赋值给某个变量的是一个对象或者数组,但由于某些原因,这个变量得到的值是null。我们却像对待正常对象或者数组的方式去访问这个变量时就会出现 NullPointerExeception


有办法避免给变量赋null值吗

看到上面的例子,有人会问:”难道不能保证让 fromSomewhere() 返回一个正常的对象吗?“

很可惜的是,有些情况还真没法保证:

  • 比如 fromSomewhere() 方法来自于其他的模块 或者 是工具库,代码不在你掌控范围内;
  • 而有时候,因为在大量的 if else 中迷失了,导致变量未被初始化
  • 新的需求加入后,导致原有代码可能出现变量值为 null 的情况
  • ...

简言之就是程序有bug!而且越复杂的程序,这种bug藏的越深。


即使变量值为null,能避免NullPointerExeception吗?

在回答可不可以之前,先问一个问题:

假如因为某些bug导致变量值为null,但程序依然对这个变量做操作,你期待程序会做什么?思考5秒~

难道你希望程序自动修复这个变量值吗?修复的时候应该给什么”正确“的值?

难道让程序若无其事地继续运行吗?后面依赖这个变量值的语句都跳过?那最后输出的结果还能是正确的吗?

假如你和我一样想不出合理的结果,那么你就明白了 运行时异常 的意义——对于NullPointerExeception这类 运行时异常(RuntimeException)计算机程序是无法自动且正确地处理的,唯一的出路就是中止运行,告诉程序员:“我搞不定这个异常 而且 我不希望输出错误的结果,你看着办吧!”

到此,你应该能理解为什么会有NullPointerExeception的设计了


怎么尽量避免NullPointerExeception?

上面是站在计算机程序的角度,它们的确无法做出抛NullPointerExeception异常以外更好的举措。

但是对于程序员而言,编写更健壮的代码的确可以避免很多的NullPointerExeception。

举几个例子:

  • 使用 Java Optional 类 https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html
  • 防御性编程 —— 对外部传入的值做非 null 检查
  • 基于防御性检查基础上,发现 null 的情况下给出合理的默认值
  • 给变量设置对应类型下合适的初始值,比如 List<String> names 的 初始值 可以设为 new ArrayList<>() 或者 Collections.emptyList() 而不是 null
  • 尝试兼容Java的其他JVM 语言 比如 Kotlin —— Kotlin 设计了一套处理 null 的机制(本质上不变)简化了防御性的代码的编写 https://kotlinlang.org/docs/reference/null-safety.html



关于为什么会有NullPointerExeception的设计的探讨到这里就结束了,但基于这个问题希望能引起读者(尤其是新手程序员)的注意 —— 不要忽略习以为常的技术细节,找时间探索背后的设计目的和原理可能会受益匪浅!