# Java `NullPointerException` - 如何在 Java 中有效处理空指针

> 原文： [https://howtodoinjava.com/java/exception-handling/how-to-effectively-handle-nullpointerexception-in-java/](https://howtodoinjava.com/java/exception-handling/how-to-effectively-handle-nullpointerexception-in-java/)

[**Java `NullPointerException`**](https://docs.oracle.com/javase/7/docs/api/java/lang/NullPointerException.html "NullPointerException")是非受检的异常，并且扩展了`RuntimeException`。 `NullPointerException`不会强迫我们使用`catch`块来处理它。 对于大多数 Java 开发人员社区而言，此异常非常像一场噩梦。 当我们最不期望它们时，它们通常会弹出。

在寻找解决此类问题的原因和最佳方法时，我也花费了大量宝贵的时间。 我将在这里编写一些[最佳实践](https://howtodoinjava.com/java-best-practices/ "best practices guides")，这些最佳实践遵循了行业惯例，一些专家讲座以及我自己的经验。

```java
Table of Contents

1\. Why NullPointerException occur in the code
2\. Common places where NullPointerException usually occur
3\. Best ways to avoid NullPointerException
4\. Available NullPointerException safe operations
5\. What if you must allow NullPointerException in some places
```

## 1\. 为什么在代码中出现`NullPointerException`

`NullPointerException`是代码中您尝试访问/修改尚未初始化的对象的情况。 从本质上讲，这意味着*对象引用变量未指向*任何位置，并且未引用任何内容或引用了`null`。 一个抛出空指针异常的示例 Java 程序。

```java
package com.howtodoinjava.demo.npe;

public class SampleNPE 
{
   public static void main(String[] args) 
   {
      String s = null;
      System.out.println( s.toString() );   // 's' is un-initialized and is null
   }
}

```

## 2\. Java `NullPointerException`通常发生的常见位置

好吧，由于各种原因，`NullPointerException`可能会出现在代码中的任何位置，但我根据自己的经验准备了最常用的地点清单。

1.  在未初始化的对象上调用方法
2.  方法中传递的参数为`null`
3.  在`null`的对象上调用`toString()`方法
4.  在不检查`null`相等性的情况下比较`if`块中的对象属性
5.  对于像 Spring 这样的依赖注入的框架来说，配置不正确
6.  在`null`的对象上使用`synchronized`
7.  链接语句，即单个语句中的多个方法调用

这不是详尽的清单。 还有其他几个地方和原因。 如果您还可以回忆起其他任何内容，请发表评论。 它也会帮助其他人（初学者）。

## 3\. 避免 Java `NullPointerException`的最佳方法

#### 3.1 三元运算符

如果[运算符](https://en.wikipedia.org/wiki/%3F:#Java "ternary operator")不为`null`，则结果为左侧的值，否则求值右侧。 它具有如下语法：

```java
boolean expression ? value1 : value2;
```

如果`expression`计算为`true`，则整个表达式将返回`value1`，否则返回`value2`。 它更像`if-else`构造，但是更有效和更具表现力。 为了防止`NullPointerException`（NPE），请像下面的代码一样使用此运算符：

```java
String str = (param == null) ? "NA" : param;
```

#### 3.2 使用 Apache Commons `StringUtils`进行字符串操作

[Apache Commons lang](https://commons.apache.org/proper/commons-lang/ "common lang") 是几个工具类的集合，用于各种操作之王。 其中之一是[`StringUtils.java`](https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/StringUtils.html "StringUtils java doc")。 使用`StringUtils.isNotEmpty()`验证作为参数传递的字符串是否为空或空字符串。 如果不为`null`或为空; 然后进一步使用它。

其他类似方法是`StringUtils.IsEmpty()`和 `StringUtils.equals()`。他们在自己的 javadocs 中声称，如果`StringUtils.isNotBlank()`抛出 NPE，则 API 中存在错误。

```java
if (StringUtils.isNotEmpty(obj.getvalue())){
    String s = obj.getvalue();
    ....
}

```

#### 3.3 尽早检查方法参数是否为空

您应该始终将输入验证置于方法的开头，以使其余代码不必处理输入错误的可能性。 因此，如果有人传递空值，那么事情将在栈的早期破裂，而不是在更难以识别根本问题的更深的位置破裂。

*在大多​​数情况下，针对快速失败行为的选择是一个不错的选择。*

#### 3.4 考虑原始类型而不是对象

当对象引用指向无内容时，将发生空问题。 因此，尽可能多地使用原始类型始终是安全的，因为它们不会受到空引用的影响。 所有原始类型都必须附加一些默认值，因此请当心。

#### 3.5 仔细考虑链接方法调用

虽然链式语句很容易在代码中查看，但它们不是 NPE 友好的。 一条分散在多行中的语句将为您提供栈跟踪中第一行的行号，无论它出现在何处。

```java
ref.method1().method2().method3().methods4();
```

这些链接的语句将仅打印“`NullPointerException`在行号`xyz`中发生”。确实很难调试此类代码。 避免这样的调用。

#### 3.6 使用`String.valueOf()`而不是`toString()`

如果您必须打印任何对象的字符串表示形式，请不要使用`object.toString()`。 这是 NPE 的非常软的目标。 而是使用`String.valueOf(object)`。
即使`object`在第二种方法中为`null`，它也不会给出异常，并且将输出“`null`”到输出流。

#### 3.7 避免从方法中返回`null`

避免 NPE 的一个很棒的技巧是返回空字符串或空集合，而不是返回`null`。 在您的应用程序中一致地执行此操作。 您会注意到，如果这样做，则不需要进行大量的空检查。

例如：

```java
List<string> data = null;

@SuppressWarnings("unchecked")
public List getDataDemo()
{
   if(data == null)
      return Collections.EMPTY_LIST; //Returns unmodifiable list
   return data;
}

```

使用上述方法的用户即使错过了空检查，也不会看到难看的 NPE。

#### 3.8 不鼓励传递空参数

我已经看到一些方法声明，其中方法需要两个或多个参数。 如果参数之一作为`null`传递，则方法以某种不同的方式起作用。 避免这种情况。

相反，您应该定义两种方法； 一个带有单个参数，第二个带有两个参数。 强制传递参数。 当您在方法内部编写应用程序逻辑时，这很有帮助，因为您可以确保方法参数不会为`null`。 因此您不会提出不必要的假设和主张。

#### 3.9 在“安全”的非空字符串上调用`String.equals(String)`

代替在下面的代码中编写的字符串比较

```java
public class SampleNPE {
   public void demoEqualData(String param) {
      if (param.equals("check me")) {
         // some code
      }
   }
}

```

像这样写上面的代码。 即使参数作为`null`传递，这也不会引起 NPE。

```java
public class SampleNPE {
   public void demoEqualData(String param) {
      if ("check me".equals(param)) // Do like this
      {
         // some code
      }
   }
}

```

## 4\. 可用的`NullPointerException`安全操作

#### 4.1 实例运算符

`instanceof`运算符是 NPE 安全的。 因此，`instanceof null`总是返回`false`。 它不会导致`NullPointerException`。 如果您记住这一事实，则可以消除混乱的条件代码。

```java
// Unnecessary code
if (data != null &amp;&amp; data instanceof InterestingData) {
}

// Less code. Better!!
if (data instanceof InterestingData) {
}

```

#### 4.2 访问类的静态成员

如果您要处理静态变量或静态方法，则即使您的引用变量指向`null`，也不会获得`null`指针异常，因为静态变量和方法调用是在编译时根据类名绑定的，并且与对象无关

```java
MyObject obj = null;
String attrib = obj.staticAttribute; //no NullPointerException because staticAttribute is static variable defined in class MyObject

```

如果您知道更多这样的语言构造，当遇到`null`时也不会失败，请告诉我。

## 5\. 如果必须在某些地方允许`NullPointerException`怎么办

《Effective Java》中的 ***Joshua bloch*** 说：“可以说，所有错误的方法调用都可以归结为非法参数或非法状态，但是其他异常通常用于某些类型的非法参数和状态。 如果调用者在某个参数中传递了`null`，该参数禁止使用`null`值，则约定将抛出`NullPointerException`而不是`IllegalArgumentException`。”

因此，如果必须在代码中的某些位置允许`NullPointerException`，则请确保**使其具有更多信息，然后通常是这样**。 看下面的例子：

```java
package com.howtodoinjava.demo.npe;

public class SampleNPE {
   public static void main(String[] args) {
      // call one method at a time
      doSomething(null);
      doSomethingElse(null);
   }

   private static String doSomething(final String param) {
      System.out.println(param.toString());
      return "I am done !!";
   }

   private static String doSomethingElse(final String param) {
      if (param == null) {
         throw new NullPointerException(
               " :: Parameter 'param' was null inside method 'doSomething'.");
      }
      System.out.println(param.toString());
      return "I am done !!";
   }
}

```

这两个方法调用的输出是这样的：

```java
Exception in thread "main" java.lang.NullPointerException
 at com.howtodoinjava.demo.npe.SampleNPE.doSomething(SampleNPE.java:14)
 at com.howtodoinjava.demo.npe.SampleNPE.main(SampleNPE.java:8)

Exception in thread "main" java.lang.NullPointerException:  :: Parameter 'param' was null inside method 'doSomething'.
 at com.howtodoinjava.demo.npe.SampleNPE.doSomethingElse(SampleNPE.java:21)
 at com.howtodoinjava.demo.npe.SampleNPE.main(SampleNPE.java:8)

```

显然，第二个栈跟踪更有用，并且使调试容易。 将来使用。

到目前为止，我对`NullPointerException`的了解已完成。 如果您知道有关该主题的其他观点，请与我们所有人分享！

学习愉快！