在 Java 中避免 NullPointerException

Posted

技术标签:

【中文标题】在 Java 中避免 NullPointerException【英文标题】:Avoiding NullPointerException in Java 【发布时间】:2010-09-21 06:06:30 【问题描述】:

我经常使用object != null 来避免NullPointerException

什么是替代品:

if (someobject != null) 
    someobject.doCalc();

【问题讨论】:

@Shervin 鼓励使用空值会使代码难以理解且可靠性降低。 不使用 null 优于此处的大多数其他建议。抛出异常,不返回或允许空值。顺便说一句 - 'assert' 关键字是无用的,因为默认情况下它是禁用的。使用始终启用的故障机制 难道没有设计模式谈到制作空对象吗?因此,当实例化一个新对象时,您总是使用这个空对象(相同的超类,相同的 [但为空] 方法),然后在需要时将该对象设置为完整对象。您可能想查看类似的问题:***.com/questions/9162371/… 以及有关空对象模式的信息:en.wikipedia.org/wiki/Null_Object_pattern 在高级代码中应该避免空值。发明空引用的托尼·霍尔(Tony Hoare)称它们为“十亿美元的错误”。看看here 了解一些想法。 似乎在 Java 8 中:static Objects.isNull(Object o) docs.oracle.com/javase/8/docs/api/java/util/Objects.html 【参考方案1】:

在我看来,这听起来像是初级到中级开发人员在某些时候往往会面临的一个相当普遍的问题:他们要么不知道,要么不信任他们参与的合约,并且防御性地过度检查空值。此外,在编写自己的代码时,他们倾向于依靠返回空值来指示某些内容,因此需要调用者检查空值。

换句话说,有两种情况会出现空值检查:

    其中 null 是合同条款中的有效响应;和

    如果不是有效响应。

(2) 很简单。使用assert 语句(断言)或允许失败(例如,NullPointerException)。断言是在 1.4 中添加的一个未被充分利用的 Java 特性。语法是:

assert <condition>

assert <condition> : <object>

其中&lt;condition&gt; 是一个布尔表达式,&lt;object&gt; 是一个对象,其toString() 方法的输出将包含在错误中。

如果条件不成立,assert 语句将引发 Error (AssertionError)。默认情况下,Java 忽略断言。您可以通过将选项 -ea 传递给 JVM 来启用断言。您可以启用和禁用单个类和包的断言。这意味着您可以在开发和测试时使用断言验证代码,并在生产环境中禁用它们,尽管我的测试显示断言几乎不会影响性能。

在这种情况下不使用断言是可以的,因为代码只会失败,如果你使用断言就会发生这种情况。唯一的区别是,如果使用断言,它可能会以更有意义的方式更快地发生,并且可能带有额外的信息,这可能会帮助您弄清楚如果您没有预料到它为什么会发生。

(1) 有点难。如果您无法控制所调用的代码,那么您将陷入困境。如果 null 是一个有效的响应,你必须检查它。

但是,如果是您控制的代码(这种情况经常发生),那就另当别论了。避免使用空值作为响应。使用返回集合的方法,这很容易:几乎总是返回空集合(或数组)而不是 null。

使用非收藏品可能会更难。以此为例:如果您有这些接口:

public interface Action 
  void doSomething();


public interface Parser 
  Action findAction(String userInput);

Parser 获取原始用户输入并找到要做的事情,也许如果您正在为某事实现命令行界面。现在,如果没有适当的操作,您可能会制定返回 null 的合同。这会导致您正在谈论的空值检查。

另一种解决方案是永远不要返回 null,而是使用 Null Object pattern:

public class MyParser implements Parser 
  private static Action DO_NOTHING = new Action() 
    public void doSomething()  /* do nothing */ 
  ;

  public Action findAction(String userInput) 
    // ...
    if ( /* we can't find any actions */ ) 
      return DO_NOTHING;
    
  

比较:

Parser parser = ParserFactory.getParser();
if (parser == null) 
  // now what?
  // this would be an example of where null isn't (or shouldn't be) a valid response

Action action = parser.findAction(someInput);
if (action == null) 
  // do nothing
 else 
  action.doSomething();

ParserFactory.getParser().findAction(someInput).doSomething();

这是一个更好的设计,因为它导致代码更简洁。

也就是说,findAction() 方法抛出一个带有有意义的错误消息的异常可能是完全合适的——尤其是在这种依赖用户输入的情况下。 findAction 方法抛出 Exception 比调用方法抛出一个简单的 NullPointerException 而没有任何解释要好得多。

try 
    ParserFactory.getParser().findAction(someInput).doSomething();
 catch(ActionNotFoundException anfe) 
    userConsole.err(anfe.getMessage());

或者,如果您认为 try/catch 机制太丑陋,而不是什么都不做,您的默认操作应该向用户提供反馈。

public Action findAction(final String userInput) 
    /* Code to return requested Action if found */
    return new Action() 
        public void doSomething() 
            userConsole.err("Action not found: " + userInput);
        
    

【讨论】:

很好的答案,尤其是提到断言。我来自 C 语言并经常使用它们。至于空对象,它们不是灵丹妙药。我经常花费数小时调试代码,结果证明这是一个正在使用的 Null 对象,当 NPE 可以更清楚地指出问题时,什么都不做(或返回其默认值)。因此,您将不得不进行“空对象检查”而不是空检查,并且没有赢得任何东西,实际上失去了清晰度。现在,如果我不能避免 null,我只是清楚地记录它,就是这样。【参考方案2】:

如果您使用(或计划使用)像 JetBrains IntelliJ IDEA、Eclipse 或 Netbeans 这样的 Java IDE 或像 findbugs 这样的工具,那么您可以使用注解来解决这个问题。

基本上,你有@Nullable@NotNull

你可以在方法和参数中使用,像这样:

@NotNull public static String helloWorld() 
    return "Hello World";

@Nullable public static String helloWorld() 
    return "Hello World";

第二个示例无法编译(在 IntelliJ IDEA 中)。

当你在另一段代码中使用第一个helloWorld()函数时:

public static void main(String[] args)

    String result = helloWorld();
    if(result != null) 
        System.out.println(result);
    

现在 IntelliJ IDEA 编译器会告诉您检查是无用的,因为 helloWorld() 函数永远不会返回 null

使用参数

void someMethod(@NotNull someParameter)  

如果你写这样的东西:

someMethod(null);

这不会编译。

最后一个使用@Nullable的例子

@Nullable iWantToDestroyEverything()  return null; 

这样做

iWantToDestroyEverything().something();

而且您可以确定这不会发生。 :)

这是一种让编译器比通常检查更多内容并强制执行更强大的合约的好方法。不幸的是,并非所有编译器都支持它。

在 IntelliJ IDEA 10.5 及更高版本中,他们添加了对任何其他 @Nullable @NotNull 实现的支持。

见博文More flexible and configurable @Nullable/@NotNull annotations

【讨论】:

@NotNull@Nullable 和其他空值注释是JSR 305 的一部分。您还可以使用它们来检测FindBugs 等工具的潜在问题。 我发现 @NotNull@Nullable 接口存在于包 com.sun.istack.internal 中,这很奇怪。 (我想我将 com.sun 与有关使用专有 API 的警告联系起来。) jetbrains 的代码可移植性无效。在绑定到 ide 级别之前我会三思而后行。就像 Jacek S 说的那样,它们是 JSR 的一部分,顺便说一句,我认为是 JSR303。 我真的不认为使用自定义编译器是解决这个问题的可行方案。 注解的好处,@NotNull@Nullable 是,当源代码由不理解它们的系统构建时,它们会很好地降级。因此,实际上,代码不可移植的论点可能是无效的 - 如果您使用支持并理解这些注释的系统,您将获得更严格的错误检查的额外好处,否则您得到的更少但您的代码仍然应该构建良好,并且运行程序的质量是相同的,因为无论如何这些注释都没有在运行时强制执行。此外,所有编译器都是自定义的 ;-)【参考方案3】:

如果不允许空值

如果您的方法被外部调用,请从以下内容开始:

public void method(Object object) 
  if (object == null) 
    throw new IllegalArgumentException("...");
  

然后,在该方法的其余部分中,您将知道object 不为空。

如果它是一个内部方法(不是 API 的一部分),只需记录它不能为 null,就是这样。

例子:

public String getFirst3Chars(String text) 
  return text.subString(0, 3);

但是,如果您的方法只是传递值,而下一个方法传递它等等,则可能会出现问题。在这种情况下,您可能需要检查上述参数。

如果允许为空

这真的取决于。如果发现我经常这样做:

if (object == null) 
  // something
 else 
  // something else

所以我分支,做两件完全不同的事情。没有丑陋的代码sn-p,因为我确实需要根据数据做两件不同的事情。例如,我应该处理输入,还是应该计算一个好的默认值?


我实际上很少使用成语“if (object != null &amp;&amp; ...”。

如果你举例说明你通常在哪里使用习语,给你举例可能会更容易。

【讨论】:

抛出 IllegalArgumentException 有什么意义?我认为 NullPointerException 会更清楚,如果您自己不进行 null 检查,也会抛出这种情况。我要么使用断言,要么什么都不用。 不太可能接受除 null 之外的所有其他值。你可能有 IllegalArgumentException、OutOfRageException 等。有时这是有道理的。有时您最终会创建许多没有任何价值的异常类,那么您只需使用 IllegalArgumentException。对空输入有一个例外,而对其他一切有另一个例外是没有意义的。 是的,我同意fail-fast-principle,但是在上面给出的示例中,值不是传递的,而是应该调用方法的对象。所以它失败的速度同样快,并且添加一个空检查只是为了抛出一个无论如何都会在同一时间和地点抛出的异常似乎并不会使调试变得更容易。 安全漏洞? JDK 充满了这样的代码。如果您不希望用户看到堆栈跟踪,则只需禁用它们。没有人暗示没有记录该行为。 mysql 是用 C 语言编写的,其中取消引用空指针是未定义的行为,与抛出异常无关。 throw new IllegalArgumentException("object==null")【参考方案4】:

哇,当我们有 57 种不同的方式来推荐 NullObject pattern 时,我几乎不想再添加另一个答案,但我认为一些对这个问题感兴趣的人可能想知道有一个关于 Java 7 的提案添加"null-safe handling"——if-not-equal-null 逻辑的简化语法。

Alex Miller 给出的示例如下所示:

public String getPostcode(Person person)   
  return person?.getAddress()?.getPostcode();  
  

?. 表示仅在左侧标识符不为 null 时取消引用,否则将表达式的其余部分计算为 null。有些人,比如 Java Posse 成员 Dick Wall 和 voters at Devoxx 真的很喜欢这个提议,但也有人反对,理由是它实际上会鼓励更多地使用 null 作为哨兵值。


更新:official proposal 用于 Java 7 中的 null 安全运算符已在 Project Coin. 下提交。语法与上面的示例略有不同,但概念相同。


更新: null-safe 运营商提案没有进入 Project Coin。因此,您不会在 Java 7 中看到这种语法。

【讨论】:

我认为这是错误的。应该有一种方法可以指定给定变量始终为非空。 更新:提案不会使Java7.见blogs.sun.com/darcy/entry/project_coin_final_five。 有趣的想法,但语法的选择很荒谬;我不希望每个关节都钉满问号的代码库。 这个运算符存在于Groovy中,所以想要使用它的人仍然可以选择它。 这是我见过的最巧妙的想法。它应该被添加到 C 语法的每一种合理的语言中。我宁愿在任何地方都“钉上问号”,而不是整天滚动屏幕或躲避“保护条款”。【参考方案5】:

如果不允许使用未定义的值:

您可以配置您的 IDE 以警告您潜在的 null 取消引用。例如。在 Eclipse 中,请参阅 Preferences > Java > Compiler > Errors/Warnings/Null analysis

如果允许未定义的值:

如果您想定义一个新的 API,其中未定义的值有意义,请使用 Option Pattern(可能对函数式语言很熟悉)。它具有以下优点:

API 中明确说明输入或输出是否存在。 编译器会强制您处理“未定义”情况。 Option is a monad,因此不需要详细的空值检查,只需使用 map/foreach/getOrElse 或类似的组合器即可安全地使用值 (example)。

Java 8 有一个内置的Optional 类(推荐);对于早期版本,有库替代方案,例如Guava 的Optional 或FunctionalJava 的Option。但是像许多函数式模式一样,在 Java 中使用 Option(甚至 8 个)会产生相当多的样板文件,您可以使用不太冗长的 JVM 语言来减少这些样板文件,例如Scala 或 Xtend。

如果您必须处理可能返回空值的 API,那么在 Java 中您将无能为力。 Xtend 和 Groovy 有 Elvis operator ?: 和 null-safe dereference operator ?.,但请注意,如果引用为 null,则返回 null,因此它只是“推迟”对 null 的正确处理。

【讨论】:

确实,Option 模式很棒。存在一些 Java 等价物。 Guava 包含一个名为 Optional 的有限版本,它省略了大部分功能性内容。在 Haskell 中,这种模式称为 Maybe。 @Luca Molteni:你说的完全正确,我已经在帖子上发表评论请求扩展它。 :) Java 8 中将提供一个可选类 ...它(还)没有地图也没有平面地图:download.java.net/jdk8/docs/api/java/util/Optional.html 可选模式不能解决任何问题;而不是一个可能为空的对象,现在你有两个。【参考方案6】:

仅适用于这种情况-

在调用 equals 方法之前不检查变量是否为空(下面的字符串比较示例):

if ( foo.equals("bar") ) 
 // ...

如果foo 不存在,将产生NullPointerException

如果你像这样比较你的Strings,你可以避免这种情况:

if ( "bar".equals(foo) ) 
 // ...

【讨论】:

我同意 - 只有在那种情况下。我不能忍受那些把它带到下一个不必要的水平并写 if (null != myVar)... 的程序员对我来说看起来很丑而且没有任何用处! 这是一个特殊的例子,可能是最常用的一般良好实践:如果您知道,请始终使用&lt;object that you know that is not null&gt;.equals(&lt;object that might be null&gt;);。如果您知道合约并且这些方法可以处理null 参数,则它适用于equals 以外的其他方法。 这是我看到的第一个真正有意义的Yoda Conditions 示例 NullPointerExceptions 抛出是有原因的。它们被抛出是因为一个对象不应该是空的。解决这个问题是程序员的工作,而不是隐藏问题。 这只是隐藏了问题。如果您稍后使用foo 怎么办?如果一个变量不应该为空,而是......您的应用需要获得 NPE,这样开发人员才能真正解决根本原因。【参考方案7】:

Java 8 带来了新的java.util.Optional 类,可以说解决了一些问题。至少可以说它提高了代码的可读性,并且在公共 API 的情况下,可以让客户端开发人员更清楚地了解 API 的合同。

他们是这样工作的:

给定类型 (Fruit) 的可选对象被创建为方法的返回类型。它可以为空或包含Fruit 对象:

public static Optional<Fruit> find(String name, List<Fruit> fruits) 
   for (Fruit fruit : fruits) 
      if (fruit.getName().equals(name)) 
         return Optional.of(fruit);
      
   
   return Optional.empty();

现在看看这段代码,我们在Fruit (fruits) 列表中搜索给定的 Fruit 实例:

Optional<Fruit> found = find("lemon", fruits);
if (found.isPresent()) 
   Fruit fruit = found.get();
   String name = fruit.getName();

您可以使用map() 运算符对可选对象执行计算或从中提取值。 orElse() 可让您为缺失值提供回退。

String nameOrNull = find("lemon", fruits)
    .map(f -> f.getName())
    .orElse("empty-name");

当然,空值/空值的检查仍然是必要的,但至少开发人员意识到该值可能为空并且忘记检查的风险是有限的。

在使用 Optional 从头构建的 API 中,只要返回值可能为空,并且仅当它不能是 null(约定)时才返回普通对象,客户端代码可能会放弃对简单对象返回值的空检查...

当然,Optional 也可以用作方法参数,在某些情况下,这可能比 5 或 10 个重载方法更好地指示可选参数。

Optional 提供了其他方便的方法,例如允许使用默认值的orElse,以及与lambda expressions 一起使用的ifPresent

我邀请您阅读这篇文章(我编写此答案的主要来源),其中很好地解释了 NullPointerException(通常是空指针)问题以及 Optional 带来的(部分)解决方案: Java Optional Objects.

【讨论】:

Google 的 guava 对 Java 6+ 有一个可选的实现。 需要强调的是,仅将 Optional 与 ifPresent() 一起使用并不会在正常的 null 检查之上增加很多价值,这一点非常重要。它的核心价值在于它是一个可以用在map/flapMap的函数链中的monad,其实现的效果类似于别处提到的Groovy中的Elvis算子。不过,即使没有这种用法,我发现 orElse/orElseThrow 语法也非常有用。 这个博客在可选winterbe.com/posts/2015/03/15/avoid-null-checks-in-java上有一个很好的条目 为什么人们倾向于这样做if(optional.isPresent()) optional.get(); 而不是optional.ifPresent(o -&gt; ...) 因此,除了 API 合同提示之外,它实际上只是为了迎合喜欢无限链接方法的函数式程序员。【参考方案8】:

根据您检查的对象类型,您可以使用 apache commons 中的一些类,例如:apache commons lang 和 apache commons collections

例子:

String foo;
...
if( StringUtils.isBlank( foo ) ) 
   ///do something

或(取决于您需要检查的内容):

String foo;
...
if( StringUtils.isEmpty( foo ) ) 
   ///do something

StringUtils 类只是众多类之一;在公地中有很多很好的类可以进行 null 安全操作。

下面是一个示例,说明在包含 apache 库时如何在 JAVA 中使用 null vallidation(commons-lang-2.4.jar)

public DOCUMENT read(String xml, ValidationEventHandler validationEventHandler) 
    Validate.notNull(validationEventHandler,"ValidationHandler not Injected");
    return read(new StringReader(xml), true, validationEventHandler);

如果你使用的是 Spring,Spring 的包中也有相同的功能,请参见 library(spring-2.4.6.jar)

关于如何使用这个来自 spring(org.springframework.util.Assert) 的静态类的示例

Assert.notNull(validationEventHandler,"ValidationHandler not Injected");

【讨论】:

你也可以使用来自 Apache Commons 的更通用的版本,在我发现的检查参数的方法开始时非常有用。 Validate.notNull(object, "object 不能为 null"); commons.apache.org/lang/apidocs/org/apache/commons/lang/… @monojohnny 是否验证使用 Assert 语句进入?我这样问是因为 Assert 可能在 JVM 上被激活/停用,建议不要在生产中使用。 不这么认为 - 我相信如果验证失败,它只会抛出 RuntimeException【参考方案9】: 如果您认为对象不应为 null(或者它是错误),请使用断言。 如果您的方法不接受空参数,请在 javadoc 中说明并使用断言。

只有当您想处理对象可能为 null 的情况时,您才需要检查 object != null...

有一个提议在 Java7 中添加新的注解来帮助使用 null / notnull 参数: http://tech.puredanger.com/java7/#jsr308

【讨论】:

不,do not use assertions in production code。【参考方案10】:

我是“快速失败”代码的粉丝。问问自己 - 在参数为空的情况下,您是否在做一些有用的事情?如果您对代码在这种情况下应该做什么没有明确的答案......即它首先不应该为 null,然后忽略它并允许抛出 NullPointerException。调用代码对 NPE 的意义与对 IllegalArgumentException 的意义一样,但如果抛出 NPE,开发人员将更容易调试和理解出了什么问题,而不是您的代码试图执行一些其他意外的意外事件逻辑 - 最终导致应用程序失败。

【讨论】:

更好地使用断言,即 Contract.notNull(abc, "abc must be non-null, did it failed to load during xyz?"); - 这比 if (abc!=null) throw new RuntimeException... 更简洁【参考方案11】:

您可能会考虑空对象是错误的情况,而不是空对象模式(它有其用途)。

当抛出异常时,检查堆栈跟踪并解决错误。

【讨论】:

问题是通常您会丢失上下文,因为 NullPointerException 并不表示 WHICH 变量为空,并且您可能有多个“.”操作在线。使用 "if (foo == null) throw new RuntimeException("foo == null")" 可以让您明确指出什么是错误的,从而为那些必须修复它的人提供更多价值。 使用 Andersen - 我希望 Java 的异常系统能够包含正在处理的变量的名称,这样 NullPointerExceptions 不仅会指示发生异常的行,而且还会指示变量姓名。这在未混淆的软件中应该可以正常工作。 我有一位教授反对方法调用链。他的理论是,您应该警惕超过 2 种方法的调用链。我不知道这是否是一个硬性规则,但它确实消除了 NPE 堆栈跟踪的大部分问题。【参考方案12】:

有时,您有对其参数进行操作的方法,这些参数定义了对称操作:

a.f(b); <-> b.f(a);

如果你知道 b 永远不能为空,你可以交换它。它对equals最有用: 而不是foo.equals("bar"); 更好"bar".equals(foo);

【讨论】:

但是你必须假设equals(可以是任何方法)将正确处理null。实际上,所有这一切都是将责任转移给其他人(或另一种方法)。 @Supericy 基本上是的,但是equals(或任何方法)无论如何都必须检查null。或者明确声明它没有。【参考方案13】:

Google 集合框架提供了一种很好且优雅的方式来实现空值检查。

在库类中有这样一个方法:

static <T> T checkNotNull(T e) 
   if (e == null) 
      throw new NullPointerException();
   
   return e;

而用法是(用import static):

...
void foo(int a, Person p) 
   if (checkNotNull(p).getAge() > a) 
      ...
   
   else 
      ...
   

...

或者在你的例子中:

checkNotNull(someobject).doCalc();

【讨论】:

嗯,有什么区别? p.getAge() 会以更少的开销和更清晰的堆栈跟踪抛出相同的 NPE。我错过了什么? 最好在你的例子中抛出一个 IllegalArgumentException("e == null") 因为它清楚地表明它是一个程序员意图的异常(以及足够的信息来实际允许维护者识别问题)。 NullPointerExceptions 应该为 JVM 保留,因为它清楚地表明这是无意的(并且通常发生在难以识别的地方) 现在是 Google Guava 的一部分。 对我来说闻起来像是过度设计。让 JVM 抛出一个 NPE,不要用这些垃圾把你的代码弄得乱七八糟。 我喜欢它并打开大多数方法和构造函数,并对参数进行显式检查;如果有错误,方法总是在前几行失败,我知道有问题的参考,但没有找到类似getThing().getItsThing().getOtherThing().wowEncapsulationIsBroken().setLol("hi");【参考方案14】:

Null 不是“问题”。它是complete 建模工具集的组成部分。软件旨在模拟世界的复杂性,而 null 则承担着它的负担。 Null 在 Java 等中表示“无数据”或“未知”。因此,为这些目的使用空值是合适的。我不喜欢“空对象”模式;我认为它会引发“who will guard the guardians”问题。 如果你问我女朋友叫什么名字,我会告诉你我没有女朋友。在 Java 语言中,我将返回 null。 另一种方法是抛出有意义的异常以指示无法(或不想)解决的问题,并将其委托给堆栈中更高的位置以重试或向用户报告数据访问错误。

    对于“未知问题”,给出“未知答案”。(从业务角度来看,这是正确的 null 安全)在使用之前在方法内检查 null 参数可以减轻多个调用者的负担在通话前检查它们。

    public Photo getPhotoOfThePerson(Person person) 
        if (person == null)
            return null;
        // Grabbing some resources or intensive calculation
        // using person object anyhow.
    
    

    先前导致正常的逻辑流程从我的照片库中获取不存在的女朋友的照片。

    getPhotoOfThePerson(me.getGirlfriend())
    

    它适合新的 Java API(期待)

    getPhotoByName(me.getGirlfriend()?.getName())
    

    虽然对于某些人来说,查找存储在数据库中的照片是相当“正常的业务流程”,但我曾经在其他一些情况下使用如下所示的配对

    public static MyEnum parseMyEnum(String value); // throws IllegalArgumentException
    public static MyEnum parseMyEnumOrNull(String value);
    

    不要讨厌输入&lt;alt&gt; + &lt;shift&gt; + &lt;j&gt;(在Eclipse 中生成javadoc)并为您的公共API 写三个额外的词。除了不阅读文档的人之外,这对所有人来说已经足够了。

    /**
     * @return photo or null
     */
    

    /**
     * @return photo, never null
     */
    

    这是相当理论上的案例,在大多数情况下,您应该更喜欢 java null 安全 API(以防它会在 10 年后发布),但 NullPointerExceptionException 的子类。 因此,它是Throwable 的一种形式,表示合理的应用程序可能想要捕获的条件(javadoc)!要使用异常的第一个最大优势并将错误处理代码与“常规”代码 (according to creators of Java) 分开,对我来说,捕获 NullPointerException 是合适的。

    public Photo getGirlfriendPhoto() 
        try 
            return appContext.getPhotoDataSource().getPhotoByName(me.getGirlfriend().getName());
         catch (NullPointerException e) 
            return null;
        
    
    

    可能会出现问题:

    问。如果getPhotoDataSource() 返回 null 怎么办? A. 这取决于业务逻辑。如果我找不到相册,我不会给你看照片。如果 appContext 没有初始化怎么办?该方法的业务逻辑支持这一点。如果相同的逻辑应该更严格,那么抛出异常是业务逻辑的一部分,应该使用显式检查 null (案例 3)。 新的 Java Null-safe API 更适合这里有选择地指定什么暗示和不暗示被初始化在程序员错误的情况下快速失败。

    问。可以执行冗余代码并获取不必要的资源。 A. 如果getPhotoByName() 尝试打开数据库连接,创建PreparedStatement 并最终使用人名作为 SQL 参数,则可能会发生这种情况。 未知问题的方法给出了一个未知的答案(案例 1)在这里有效。在获取资源之前,该方法应检查参数并在需要时返回“未知”结果。

    问。由于尝试关闭打开,这种方法会降低性能。 A. 软件首先应该易于理解和修改。只有在此之后,才可以考虑性能,并且仅在需要时才考虑!和需要的地方! (source) 等)。

    附言。这种方法的使用与将错误处理代码与“常规”代码分开原则在某些地方使用是合理的一样合理。考虑下一个例子:

    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) 
        try 
            Result1 result1 = performSomeCalculation(predicate);
            Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
            Result3 result3 = performThirdCalculation(result2.getSomeProperty());
            Result4 result4 = performLastCalculation(result3.getSomeProperty());
            return result4.getSomeProperty();
         catch (NullPointerException e) 
            return null;
        
    
    
    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) 
        SomeValue result = null;
        if (predicate != null) 
            Result1 result1 = performSomeCalculation(predicate);
            if (result1 != null && result1.getSomeProperty() != null) 
                Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());
                if (result2 != null && result2.getSomeProperty() != null) 
                    Result3 result3 = performThirdCalculation(result2.getSomeProperty());
                    if (result3 != null && result3.getSomeProperty() != null) 
                        Result4 result4 = performLastCalculation(result3.getSomeProperty());
                        if (result4 != null) 
                            result = result4.getSomeProperty();
                        
                    
                
            
        
        return result;
    
    

    PPS。对于那些快速投反对票(而且阅读文档不那么快)的人,我想说我一生中从未遇到过空指针异常(NPE)。但这种可能性是由 Java 创建者有意设计的,因为 NPE 是 Exception 的子类。我们在 Java 历史上有一个先例,ThreadDeathError 不是因为它实际上是一个应用程序错误,而仅仅是因为它不打算被捕获!与ThreadDeath 相比,Error 适合多少 NPE!但事实并非如此。

    仅在业务逻辑暗示时检查“无数据”。

    public void updatePersonPhoneNumber(Long personId, String phoneNumber) 
        if (personId == null)
            return;
        DataSource dataSource = appContext.getStuffDataSource();
        Person person = dataSource.getPersonById(personId);
        if (person != null) 
            person.setPhoneNumber(phoneNumber);
            dataSource.updatePerson(person);
         else 
            Person = new Person(personId);
            person.setPhoneNumber(phoneNumber);
            dataSource.insertPerson(person);
        
    
    

    public void updatePersonPhoneNumber(Long personId, String phoneNumber) 
        if (personId == null)
            return;
        DataSource dataSource = appContext.getStuffDataSource();
        Person person = dataSource.getPersonById(personId);
        if (person == null)
            throw new SomeReasonableUserException("What are you thinking about ???");
        person.setPhoneNumber(phoneNumber);
        dataSource.updatePerson(person);
    
    

    如果 appContext 或 dataSource 未初始化,未处理的运行时 NullPointerException 将终止当前线程并由Thread.defaultUncaughtExceptionHandler 处理(供您定义和使用您喜欢的记录器或其他通知机制)。如果未设置,ThreadGroup#uncaughtException 会将堆栈跟踪打印到系统错误。应该监视应用程序错误日志并为每个未处理的异常打开 Jira 问题,这实际上是应用程序错误。程序员应该在初始化东西的某个地方修复错误。

【讨论】:

捕获NullPointerException 并返回null 很难调试。无论如何,你最终都会得到 NPE,而且很难弄清楚最初是什么是空的。 如果我有声望,我会投反对票。 null 不仅不是必需的,而且是类型系统中的一个漏洞。将树分配给列表是类型错误,因为树不是列表类型的值;按照同样的逻辑,分配 null 应该是一个类型错误,因为 null 不是 Object 类型的值,也不是任何有用的类型。 Even the man that invented null 认为这是他的“十亿美元的错误”。 “一个可能是 T 或无类型值的值”的概念是它自己的类型,应该这样表示(例如,Maybe 或 Optional)。 从 "Maybe 或 Optional" 开始,您仍然需要编写像 if (maybeNull.hasValue()) ... 这样的代码,那么与 if (maybeNull != null)) ... 有什么区别? 至于“捕获 NullPointerException 并返回 null 对调试来说是可怕的。无论如何你最终都会遇到 NPE,而且很难弄清楚最初是什么是 null”。我完全同意!在这些情况下,如果业务逻辑暗示数据就地,您应该编写十几个“if”语句或抛出 NPE,或者使用新 Java 中的 null-safe 运算符。但是在某些情况下,我不关心什么确切的步骤给我 null。例如,当您确实希望数据可能丢失时,在屏幕上显示之前为用户计算一些值。 @MykhayloAdamovych:Maybe&lt;T&gt;Optional&lt;T&gt; 的好处并不在于您的T 可能为空,而是在它永远不应该为空的情况下。如果您有一个明确表示“此值可能为空 - 谨慎使用”的类型,并且您始终如一地使用和返回这种类型,那么每当您在您的代码,您可以假设它永远不会为空。 (当然,如果编译器可以强制执行,这将更加有用。)【参考方案15】:

Java 7 有一个新的 java.util.Objects 实用程序类,其中有一个 requireNonNull() 方法。如果它的参数为空,所有这一切都会抛出一个NullPointerException,但它会稍微清理一下代码。示例:

Objects.requireNonNull(someObject);
someObject.doCalc();

该方法对checking在构造函数中赋值之前最有用,每次使用它可以节省三行代码:

Parent(Child child) 
   if (child == null) 
      throw new NullPointerException("child");
   
   this.child = child;

变成

Parent(Child child) 
   this.child = Objects.requireNonNull(child, "child");

【讨论】:

实际上,您的示例构成了代码膨胀:第一行是多余的,因为 NPE 将在第二行抛出。 ;-) 是的。一个更好的例子是第二行是doCalc(someObject) 视情况而定。如果您是 doCalc() 的作者,我建议将检查放入该方法的主体(如果可能)。然后你很可能会调用 someObject.someMethod() 再次不需要检查空值。 :-) 好吧,如果您不是doCalc() 的作者,并且在给定 null 时它不会立即抛出 NPE,那么您需要检查 null 并自己抛出 NPE。这就是Objects.requireNonNull() 的用途。 这不仅仅是代码膨胀。最好提前检查,而不是通过会导致副作用或使用时间/空间的方法进行一半。【参考方案16】:

最终,彻底解决这个问题的唯一方法是使用不同的编程语言:

在 Objective-C 中,您可以执行相当于在 nil 上调用方法的操作,但绝对不会发生任何事情。这使得大多数空检查变得不必要,但它会使错误更难诊断。 在Nice(Java 派生语言)中,所有类型都有两个版本:潜在空版本和非空版本。您只能在非空类型上调用方法。通过显式检查 null 可以将可能为 null 的类型转换为非 null 类型。这样可以更轻松地了解哪些地方需要进行空值检查,哪些地方不需要。

【讨论】:

我对 Nice 不熟悉,但 Kotlin 实现了相同的想法,在语言的类型系统中构建了可空和非空类型。比 Optionals 或 null Object 模式更简洁。【参考方案17】:

确实是 Java 中的常见“问题”。

首先,我对此的看法:

我认为在传递 NULL 时“吃”一些东西是不好的,而 NULL 不是有效值。如果您没有以某种错误退出该方法,那么这意味着您的方法没有出错,这是不正确的。然后你可能在这种情况下返回 null,在接收方法中你再次检查 null,它永远不会结束,你最终会得到 "if != null" 等等。

因此,恕我直言,null 必须是一个严重错误,它会阻止进一步执行(也就是说,null 不是有效值)。

我解决这个问题的方法是这样的:

首先,我遵循这个约定:

    所有公共方法/API 始终检查其参数是否为空 所有私有方法都不检查 null,因为它们是受控方法(如果上面没有处理,就让 nullpointer 异常死掉) 唯一不检查 null 的其他方法是实用程序方法。它们是公开的,但是如果你出于某种原因调用它们,你就会知道你传递了什么参数。这就像在不提供水的情况下试图在水壶中烧开水......

最后,在代码中,公共方法的第一行是这样的:

ValidationUtils.getNullValidator().addParam(plans, "plans").addParam(persons, "persons").validate();

注意addParam()返回self,这样可以添加更多参数进行检查。

如果任何参数为空,方法 validate() 将抛出选中的 ValidationException(选中或未选中更多是设计/品味问题,但我的 ValidationException 已选中)。

void validate() throws ValidationException;

例如,如果“plans”为空,则消息将包含以下文本:

"参数 [plans] 遇到非法参数值 null"

如您所见,用户消息需要 addParam() 方法中的第二个值(字符串),因为您无法轻松检测传入的变量名称,即使使用反射(无论如何都不是本文的主题.. .).

是的,我们知道,在这一行之外,我们将不再遇到空值,因此我们只需安全地调用这些对象上的方法。

这样,代码干净,易于维护和可读。

【讨论】:

当然。只是抛出错误和崩溃的应用程序质量更高,因为毫无疑问它们何时不工作。吞下错误的应用程序充其量会优雅地降级,但通常不会以难以注意到且无法修复的方式工作。当问题被发现时,它们就更难调试了。【参考方案18】:

问这个问题表明您可能对错误处理策略感兴趣。如何以及在何处处理错误是一个普遍存在的架构问题。有几种方法可以做到这一点。

我最喜欢的:允许异常泛滥 - 在“主循环”或其他具有适当职责的函数中捕获它们。检查错误情况并适当处理它们可以被视为一项特殊责任。

当然也可以看看面向方面的编程 - 他们有巧妙的方法将if( o == null ) handleNull() 插入到您的字节码中。

【讨论】:

【参考方案19】:

除了使用assert,您还可以使用以下内容:

if (someobject == null) 
    // Handle null here then move on.

这比:

if (someobject != null) 
    .....
    .....



    .....

【讨论】:

嗯,为什么?请不要有任何防备,我只是想了解更多关于 Java 的知识:) @Mudu 作为一般规则,我更喜欢 if 语句中的表达式是更“肯定”的语句,而不是“否定”语句。因此,如果我看到if (!something) x(); else y(); ,我会倾向于将其重构为if (something) y(); else x(); (尽管有人可能会争辩说!= null 是更积极的选择......)。但更重要的是,代码的重要部分没有包含在 s 中,并且对于大多数方法,您的缩进减少了一级。我不知道这是否是 fastcodejava 的推理,但那是我的。 这也是我倾向于做的事情。在我看来,保持代码整洁。 @MatrixFrog 是的,这使代码更清晰易读,因为您首先看到了重要的代码(当一切正常运行时)。【参考方案20】:

永远不要使用 null。不允许。

在我的类中,大多数字段和局部变量都有非空默认值,我在代码中的任何地方都添加了合同声明(始终开启断言)以确保执行此操作(因为它更简洁,更具表现力而不是让它作为 NPE 出现,然后必须解析行号等)。

一旦我采用了这种做法,我发现问题似乎可以自行解决。你会在开发过程的早期偶然发现一些东西,并意识到你有一个弱点......更重要的是......它有助于封装不同模块的关注点,不同的模块可以相互“信任”,并且不再乱扔垃圾带有if = null else 结构的代码!

这是防御性编程,从长远来看会产生更简洁的代码。始终清理数据,例如在这里通过执行严格的标准,问题就会消失。

class C 
    private final MyType mustBeSet;
    public C(MyType mything) 
       mustBeSet=Contract.notNull(mything);
    
   private String name = "<unknown>";
   public void setName(String s) 
      name = Contract.notNull(s);
   



class Contract 
    public static <T> T notNull(T t)  if (t == null)  throw new ContractException("argument must be non-null"); return t; 

合约就像迷你单元测试,即使在生产中也始终在运行,当事情失败时,你知道为什么,而不是你必须以某种方式找出随机 NPE。

【讨论】:

为什么会被否决?以我的经验,这远远优于其他方法,很想知道为什么不 我同意,这种方法可以防止与空值相关的问题,而不是通过到处散布代码空值检查来修复它们。 这种方法的问题是,如果从未设置名称,它的值是“”,其行为类似于设置值。现在假设我需要检查名称是否从未设置(未知),我必须与特殊值“”进行字符串比较。 真正的好点史蒂夫。我经常做的是将该值作为常数,例如public static final String UNSET="__unset" ... private String field = UNSET ... 然后 private boolean isSet() return UNSET.equals(field); 恕我直言,这是 Null 对象模式的实现,带有自己的 Optional(合同)实现。它在持久性类中的表现如何?我认为在这种情况下不适用。【参考方案21】:

Guava 是 Google 的一个非常有用的核心库,它有一个很好用的 API 来避免空值。我觉得UsingAndAvoidingNullExplained 很有帮助。

如 wiki 中所述:

Optional&lt;T&gt; 是一种将可以为空的 T 引用替换为 非空值。 Optional 可以包含非空 T 引用 (在这种情况下,我们说引用是“存在的”),或者它可能包含 什么都没有(在这种情况下,我们说引用是“不存在的”)。它从来不是 表示“包含空值”。

用法:

Optional<Integer> possible = Optional.of(5);
possible.isPresent(); // returns true
possible.get(); // returns 5

【讨论】:

@CodyGuldner 对,科迪。我提供了链接中的相关引用以提供更多背景信息。【参考方案22】:

对于每个 Java 开发人员来说,这是一个非常常见的问题。因此,Java 8 提供了官方支持来解决这些问题,而不会产生混乱的代码。

Java 8 引入了java.util.Optional&lt;T&gt;。它是一个容器,可能包含也可能不包含非空值。 Java 8 提供了一种更安全的方法来处理在某些情况下值可能为 null 的对象。它的灵感来自Haskell 和Scala 的想法。

简而言之,Optional 类包括显式处理值存在或不存在的情况的方法。但是,与空引用相比的优势在于 Optional 类迫使您考虑值不存在时的情况。因此,您可以防止意外的空指针异常。

在上面的示例中,我们有一个家庭服务工厂,它为家庭中的多个可用设备返回一个句柄。但这些服务可能可用也可能不可用/功能;这意味着它可能会导致 NullPointerException。与其在使用任何服务之前添加一个 null if 条件,不如将其包装到 Optional 中。

包装到选项

让我们考虑一种从工厂获取服务引用的方法。与其返回服务引用,不如用 Optional 包装它。它让 API 用户知道返回的服务可能或可能不可用/功能,防御性使用

public Optional<Service> getRefrigertorControl() 
      Service s = new  RefrigeratorService();
       //...
      return Optional.ofNullable(s);
   

如您所见,Optional.ofNullable() 提供了一种简单的方法来包装引用。还有另一种方法可以获取 Optional 的引用,Optional.empty()Optional.of()。一个用于返回一个空对象而不是重新调整 null,另一个用于分别包装一个不可为空的对象。

那么它究竟如何帮助避免空检查?

一旦你包装了一个引用对象,Optional 提供了许多有用的方法来调用包装的引用上的方法,而无需 NPE。

Optional ref = homeServices.getRefrigertorControl();
ref.ifPresent(HomeServices::switchItOn);

Optional.ifPresent 如果给定的 Consumer 是非空值,则使用引用调用它。否则,它什么也不做。

@FunctionalInterface
public interface Consumer<T>

表示接受单个输入参数并且不返回结果的操作。与大多数其他功能接口不同,消费者预计将通过副作用进行操作。 它是如此干净且易于理解。在上面的代码示例中,HomeService.switchOn(Service) 会在 Optional 持有引用为非 null 时被调用。

我们经常使用三元运算符来检查空条件并返回替代值或默认值。 Optional 提供了另一种处理相同条件而不检查 null 的方法。如果 Optional 具有空值,则 Optional.orElse(defaultObj) 返回 defaultObj。让我们在示例代码中使用它:

public static Optional<HomeServices> get() 
    service = Optional.of(service.orElse(new HomeServices()));
    return service;

现在 HomeServices.get() 做同样的事情,但以更好的方式。它检查服务是否已经初始化。如果是则返回相同或创建一个新的新服务。 Optional.orElse(T) 有助于返回默认值。

最后,这是我们的 NPE 以及空检查免费代码:

import java.util.Optional;
public class HomeServices 
    private static final int NOW = 0;
    private static Optional<HomeServices> service;

public static Optional<HomeServices> get() 
    service = Optional.of(service.orElse(new HomeServices()));
    return service;


public Optional<Service> getRefrigertorControl() 
    Service s = new  RefrigeratorService();
    //...
    return Optional.ofNullable(s);


public static void main(String[] args) 
    /* Get Home Services handle */
    Optional<HomeServices> homeServices = HomeServices.get();
    if(homeServices != null) 
        Optional<Service> refrigertorControl = homeServices.get().getRefrigertorControl();
        refrigertorControl.ifPresent(HomeServices::switchItOn);
    


public static void switchItOn(Service s)
         //...
    

完整的帖子是NPE as well as Null check-free code … Really?

【讨论】:

上面的代码中有一个空检查-if(homeServices != null) 可以改成homeServices.ifPresent(h -&gt; //action);【参考方案23】:

我喜欢 Nat Pryce 的文章。以下是链接:

Avoiding Nulls with Polymorphic Dispatch Avoiding Nulls with "Tell, Don't Ask" Style

在文章中还有一个指向 Java Maybe 类型的 Git 存储库的链接,我觉得这很有趣,但我认为单独使用它并不能减少 检查代码膨胀。在网上做了一些研究后,我认为 != null 代码膨胀主要可以通过精心设计来减少。

【讨论】:

Michael Feathers 写了一篇简短而有趣的文章,介绍了您提到的方法:manuelp.newsblur.com/site/424 如果我对这个答案有什么最欣赏的地方,那就是“精心设计”的提示。无论归结为编写代码、修复错误还是检查无效性,在代码中进行整体良好的设计和组织是非常重要的,以避免由于长期以来做出的错误设计选择而导致我们今天仍然必须处理的许多冗余以前...【参考方案24】:

我已经尝试过NullObjectPattern,但对我来说并不总是最好的方法。有时“不采取行动”是不合适的。

NullPointerException 是一个运行时异常,这意味着它是开发人员的错,如果有足够的经验,它会告诉你错误的确切位置。

现在来回答:

尽量使您的所有属性及其访问器尽可能私有,或者完全避免将它们暴露给客户端。当然,您可以在构造函数中包含参数值,但是通过缩小范围,您不会让客户端类传递无效值。如果您需要修改这些值,您可以随时创建一个新的object。您只检查构造函数中的值一次,在其余方法中,您几乎可以确定这些值不为空。

当然,经验是理解和应用这个建议的更好方式。

字节!

【讨论】:

【参考方案25】:

Java 8 或更新版本的最佳替代方案可能是使用Optional 类。

Optional stringToUse = Optional.of("optional is there");
stringToUse.ifPresent(System.out::println);

这对于可能的空值的长链特别方便。示例:

Optional<Integer> i = Optional.ofNullable(wsObject.getFoo())
    .map(f -> f.getBar())
    .map(b -> b.getBaz())
    .map(b -> b.getInt());

关于如何在 null 上抛出异常的示例:

Optional optionalCarNull = Optional.ofNullable(someNull);
optionalCarNull.orElseThrow(IllegalStateException::new);

Java 7 引入了Objects.requireNonNull 方法,该方法在需要检查非空值时非常方便。示例:

String lowerVal = Objects.requireNonNull(someVar, "input cannot be null or empty").toLowerCase();

【讨论】:

您说过,“这对于可能的空值的长链特别方便”。你能解释一下怎么做吗?【参考方案26】:

我可以更笼统地回答它吗!

我们通常在方法以我们不期望的方式获取参数时会遇到这个问题(错误的方法调用是程序员的错)。例如:您希望得到一个对象,而不是得到一个空值。您希望得到一个至少包含一个字符的字符串,而不是得到一个空字符串 ...

所以没有区别:

if(object == null)
   //you called my method badly!

if(str.length() == 0)
   //you called my method badly again!

在我们执行任何其他功能之前,他们都希望确保我们收到了有效的参数。

正如在其他一些答案中提到的,为避免上述问题,您可以遵循 按合同设计 模式。请参阅http://en.wikipedia.org/wiki/Design_by_contract。

要在 java 中实现此模式,您可以使用核心 java 注释,如 javax.annotation.NotNull 或使用更复杂的库,如 Hibernate Validator

只是一个示例:

getCustomerAccounts(@NotEmpty String customerId,@Size(min = 1) String accountType)

现在您可以安全地开发方法的核心功能,而无需检查输入参数,它们可以保护您的方法免受意外参数的影响。

您可以更进一步,确保在您的应用程序中只能创建有效的 pojo。 (来自休眠验证器站点的示例)

public class Car 

   @NotNull
   private String manufacturer;

   @NotNull
   @Size(min = 2, max = 14)
   private String licensePlate;

   @Min(2)
   private int seatCount;

   // ...

【讨论】:

javax 根据定义,不是“核心 Java”。【参考方案27】:

我高度无视建议在每种情况下都使用空对象的答案。这种模式可能会破坏契约,把问题埋得越来越深,而不是解决问题,更不用说使用不当会产生另一堆需要未来维护的样板代码。

实际上,如果从方法返回的某些内容可能为 null,并且调用代码必须对此做出决定,则应该有一个更早的调用来确保状态。

另外请记住,如果不小心使用,空对象模式会占用大量内存。为此 - NullObject 的实例应该在所有者之间共享,而不是每个所有者的唯一实例。

另外,我不建议在类型是原始类型表示的情况下使用这种模式——比如不是标量的数学实体:向量、矩阵、复数和 POD(普通旧数据)对象,它们的意思是以 Java 内置类型的形式保存状态。在后一种情况下,您最终会调用具有任意结果的 getter 方法。例如 NullPerson.getName() 方法应该返回什么?

为了避免荒谬的结果,值得考虑这种情况。

【讨论】:

使用“hasBackground()”的解决方案有一个缺点——它不是线程安全的。如果需要调用两个方法而不是一个方法,则需要在多线程环境下同步整个序列。 @pkalinow 你做了一个人为的例子只是为了指出这个解决方案有一个缺点。如果代码不打算在多线程应用程序中运行,那么没有缺点。我可以给你大概 90% 的代码不是线程安全的。我们在这里不是在谈论代码的这一方面,而是在谈论设计模式。多线程本身就是一个主题。 当然在单线程应用程序中这不是问题。我给出了该评论,因为有时这是一个问题。 @pkalinow 如果您仔细研究这个主题,您会发现空对象设计模式无法解决多线程问题。所以这无关紧要。老实说,我已经找到了适合这种模式的地方,所以我原来的答案实际上有点错误。【参考方案28】:
    永远不要将变量初始化为 null。 如果 (1) 不可行,请将所有集合和数组初始化为空集合/数组。

在您自己的代码中执行此操作,您可以避免 != null 检查。

大多数时候,空值检查似乎可以保护集合或数组上的循环,因此只需将它们初始化为空,您就不需要任何空值检查。

// Bad
ArrayList<String> lemmings;
String[] names;

void checkLemmings() 
    if (lemmings != null) for(lemming: lemmings) 
        // do something
    




// Good
ArrayList<String> lemmings = new ArrayList<String>();
String[] names = ;

void checkLemmings() 
    for(lemming: lemmings) 
        // do something
    

这有一点点开销,但对于更简洁的代码和更少的 NullPointerExceptions,这是值得的。

【讨论】:

***.com/questions/1386275/… +1 我同意这一点。您永远不应该返回一半已初始化的对象。 Jaxb 相关代码和 bean 代码是 notiroius 的。这是不好的做法。所有集合都应该被初始化,并且所有对象都应该存在(理想情况下)没有空引用。考虑一个包含集合的对象。检查对象不为空、集合不为空、集合不包含空对象是不合理和愚蠢的。【参考方案29】:

这是大多数开发人员最常遇到的错误。

我们有很多方法来处理这个问题。

方法一:

org.apache.commons.lang.Validate //using apache framework

notNull(Object object, String message)

方法2:

if(someObject!=null) // simply checking against null

方法 3:

@isNull @Nullable  // using annotation based validation

方法 4:

// by writing static method and calling it across whereever we needed to check the validation

static <T> T isNull(someObject e)  
   if(e == null)
      throw new NullPointerException();
   
   return e;

【讨论】:

广告。 4. 它不是很有用——当你检查一个指针是否为空时,你可能想在它上面调用一个方法。在 null 上调用方法会产生相同的行为 - NullPointerException。【参考方案30】:

总之避免声明

if (object != null) 
    ....

    从 java 7 开始,您可以使用 Objects 方法:

    Objects.isNull(对象)

    Objects.nonNull(object)

    Objects.requireNonNull(object)

    Objects.equals(object1, object2)

    从 java 8 开始,您可以使用可选类 (when to use)

object.ifPresent(obj -&gt; ...);java 8

object.ifPresentOrElse(obj -&gt; ..., () -&gt; ...);java 9

    依赖方法契约 (JSR 305) 并使用 Find Bugs。使用注释@javax.annotation.Nullable@javax.annotation.Nonnnul 标记您的代码。还有先决条件。

    前置条件.checkNotNull(object);

    在特殊情况下(例如字符串和集合),您可以使用 apache-commons(或 Google guava)实用方法:

public static boolean isEmpty(CharSequence cs) //apache CollectionUtils

public static boolean isEmpty(Collection coll) //apache StringUtils

public static boolean isEmpty(Map map) //apache MapUtils

public static boolean isNullOrEmpty(@Nullable String string) //番石榴字符串

    当你需要在 null 使用 apache commons lang 时指定默认值时

public static Object defaultIfNull(Object object, Object defaultValue)

【讨论】:

解释得很好

以上是关于在 Java 中避免 NullPointerException的主要内容,如果未能解决你的问题,请参考以下文章

片段之间的共享元素转换

Xmpp String utils 经常使应用程序崩溃

在 Java 中避免同步(this)?

在 Java 中避免同步(this)?

在 java-8 中处理或避免 NPE 的最佳方法是啥?

在 Java 中避免 NullPointerException