时间:2010-07-23 21:46:43

标签: java exception-handling nullpointerexception throw

19 个答案:

答案 0 :(得分:61)

答案 1 :(得分:41)

答案 2 :(得分:27)

答案 3 :(得分:12)

NullPointerException的问题是,当你忘记来检查某些东西是否为null或者给出错误的参数为null时,它会发生,而不应该。

根据我的经验,Java程序员非常快速地了解到这个异常是由代码中的错误引起的,因此手动抛出它会对大多数造成极大的混乱。当你传递不可接受的参数时(例如null,其中某些东西不能为null),IllegalArgumentException更好。

它还触发了另一种启发式方法。 NPE =有人在这里的代码中出错,IllegalArgumentException =给该方法的对象无效。

另一方面,javadoc告诉:

  

应用程序应抛出此类的实例以指示
  null对象的其他非法用途。

因此投掷NPE将是合法,但这不是常见做法,因此我建议IllegalArgumentException

答案 4 :(得分:7)

答案 5 :(得分:5)

答案 6 :(得分:3)

答案 7 :(得分:3)

答案 8 :(得分:3)

http://pmd.sourceforge.net/pmd-5.0.1/rules/java/strictexception.html
“避免抛出NullPointerExceptions。这些令人困惑,因为大多数人会认为虚拟机会抛出它。考虑使用IllegalArgumentException代替;这将被视为程序员发起的异常。”

答案 9 :(得分:1)

答案 10 :(得分:1)

答案 11 :(得分:0)

我同意前面的答案中的声明,NPE是代码中的错误,不应该抛出,但开发人员应该修复意外的null。然而,这种状态可以通过测试大部分时间来预防。

在7年之前问过这个问题,但现在我们在java 8中有了Optional,这个功能可以防止NPE。

最后一个解决方案,在我看来,你应该检查null上的对象,如果它是相等的,那么抛出你自己的异常,描述发生的事情。

答案 12 :(得分:0)

是的,没关系,但我认为这是让它发生的更好决定。

Java程序员和null的问题在于人们来自C / C ++背景,其中NULL意味着很多不同。在C / C ++中,取消引用NULL(或狂野)指针是一个严重的问题,可能会导致奇怪的内存问题或程序崩溃(显然不可取)。如果你能够摆脱C / C ++的思维方式,并且你意识到你有这个额外的层,JVM,它为你处理这个条件,你开始考虑一点点不同的NULL。

在C ++中,我们有引用,例如,永远不能分配NULL。在Java中,没有引用,但Java对象参数更像是C ++指针。但是在Java中有很多情况,其中一个方法隐式地应该 NOT 接收参数的空值!那么,我们该怎么办?

像在C ++中一样处理Java中的null的问题是,这导致空检查 EVERYWHERE ,而在C ++中,您只需声明一个方法来获取引用,该方法明确指出它不接受NULL。很快,每个方法都必须进行一次健全性检查,以便在此时断言程序的状态,造成混乱。

假设方法的默认契约是null对参数值无效,那么工作状态要好得多。

为什么呢?那么,让我们来看看当这样的方法接收null作为参数值时会发生什么。 a)也许它没关系,因为它不会将价值作为其行为的一部分取消引用。在这种情况下,没有任何反应。 b)取消引用该值。在这种情况下,来自JVM的行为正是我们想要的:抛出一个异常,表明由于参数值为null而违反了方法的契约,并且它包含一个堆栈跟踪,将我们一直带到以这种方式使用值的方法中的行。

人们对NPE有疑问,因为他们认为当你在日志中看到NPE时,就意味着有人搞砸了#34;。但让我们暂时考虑一下。 NPE作为fuckup的指标实际上与预期行为相反的区别是什么?我认为使用NPE作为预期行为的主要区别(和优势)是它不是指出它发生的方法,而是指违者方法违反方法的合同。这是更有用的信息。如果我们只是简单地检查null并抛出一个不同的异常,我们可能会误以为观察到的行为是一个预期的错误,当真正的调用者违反了方法的契约时。恭喜你,你正确地预测了调用者在调用方法时可能会如何搞砸 - 但是你所做的一切都让自己误入歧途,了解异常的真正原因是什么 - 或者至多,你是否正在使用两个不同的异常类来表示相同的事情,并在此期间大量弄脏代码与不必要的垃圾。

因此,当涉及到它时,人们认为NPE是禁忌。从字面上看,人们不会允许它被抛出,因为它带有一些羞耻感 - 好像你不够聪明,因为你没有猜到一个值是无效的。好吧,我得到了你们的新闻,你们只是编写了更多无用的代码来做同样的事情。

一些例子:

public void foo(Object o) {
  if (o == null) {
    throw new IGotchaException("HA! You're an IDIOT! I knew it!!");
  }
  o.bar();
}

public void foo(Object o) {
  o.bar();
}

^政治上不同。功能上,不是那么多。

public void foo(int a, long b, double c, Object o) {
  if (o == null) {
    throw new IllegalArgumentException("Oh. Uh. Well, you've passed me null here. I... I'm not sure where to go from here because this object is kind of required for this function to do what it's supposed to do. Soooo... wouldja mind reworking your code a little bit so as to not pass null to this function as a parameter?! That'd be great thanks. Oh by the way, it's cause we deference o 40 lines below here");
  }
  // ...
  o.doSomethingWithA(a);
}

public void foo(int a, long b, double c, Object o) {
  // ...
  o.doSomethingWithA(a);
  // NullPointerException, line 40, e.g. it wasn't OK to pass null for o you lunkhead
}

^也许以牺牲很多烦人的代码为代价来节省一些CPU周期。但是,我们在第二种情况下做的比较较少。

public void foo(Object a, Object b, Object c, Object d) {
  if (a == null) throw IllegalArgumentException("jackass");
  if (b == null) throw IllegalArgumentException("jackass");
  if (c == null) throw IllegalArgumentException("jackass");
  // But d is totally OK!
  // ...
  c.setSomeValueThatMayBeNull(d);
}

public void foo(Object a, Object b, Object c, Object d) {
  // ...
  c.setSomeValueThatMayBeNull(d);
  // Throws null pointer exception if c is null, but not if d is null. Which is basically the same as above
}

^合同是隐含在语句中的,而不是方法开头的异常情况。没有其他缺点。

public void foo(Object o) {
  if (o == null) {
    doTheBoogie();
  } else {
    doTheRobot();
  }
}

^ Bad

public void foo(Object o, int b) {
  Bar value = o.findSomethingWhichMayExist(b);
  if (value == null)
    return;
  value.doSomething();
}

^使用null返回值表示缺少值。行。

人们遇到NPE问题的另一个原因是他们不知道如何处理异常。 NPE永远不应该成为一个障碍。适当的行为是捕获RuntimeException,可能是在调用堆栈中的更高(或更低,取决于你看到它)的级别,陷阱并在" main"之前报告它。也就是说,假设您正在开发那种需要更具弹性且在NPE发生时无法崩溃的程序。

底线:不要指望传递方法参数的空值是有效的。并且绝对不要为明确接受null的方法创建一个契约,并将其视为有效值。但是,允许空指针异常发生,让代码失败,或者当它自然无关紧要时不会失败。

答案 13 :(得分:0)

  

当有后置条件时,方法的返回值不能为空,可以做什么?

后置条件意味着如果不满足条件,则该方法存在错误。在代码中表达这一点的方法是在后置条件下使用assert。直接抛出异常,例如NullPointerExceptionIllegalStateException,会有点误导,因而被误导。

  

以编程方式抛出NullPointerException是否可以?

NPE的Java API文档是肯定的,但是,根据本页面上的投票判断,3:1的大多数开发人员都说没有。所以我说这取决于你工作组中的惯例。

API doc首先列出了JVM引发NPE的情况,因为代码试图在需要某种对象(如调用方法或访问字段)的空引用上调用操作,并{{1} }不是一个对象。然后说明:

  

应用程序应抛出此类的实例以指示null对象的其他非法用法。

有趣的是,null在这里被称为“对象”,而不是。这让我想起名字null对于没有指针的语言而言是奇怪的。 (这可能与Microsoft .NET类库中的NullPointerException一样。)

那么我们应该忽略这个计数的API文档吗?我不这么认为。类库确实使用文档中描述的NPE,例如NullReferenceException

  

除非另有说明,否则将java.nio.channels参数传递给此包中任何类或接口中的构造函数或方法将导致抛出null

这不是由JVM生成的NPE,而是带有附加错误消息的编码NPE,指出哪个参数为NullPointerException(如null)。 (通过执行"in" is null!可以查看代码,查找javap -c -p java.nio.channels.Channels | more。)并且有许多类以这种方式使用NPE,基本上是private static void checkNotNull的特殊情况。

所以在仔细研究了一下并思考它之后,我发现这是对NPE的一个很好的用法,因此我同意API文档和少数Java开发人员(根据本页的投票)你有资格和权利在你自己的代码中使用NPE,就像Java类库一样,也就是提供一个错误消息,这在JVM生成的NPE中明显缺失,这就是为什么告诉二者没有问题的原因。各种NPE。

要解决NPE将在未来发展的另一个小问题:尽早捕获错误而不是允许JVM继续执行程序,可能涉及磁盘或网络I / O是非常有意义的(和延迟),并产生不必要的大堆栈跟踪。

答案 14 :(得分:0)

正如约书亚布洛赫曾经说过的那样:“无聊!” :)每当有空是我的脸,我尝试使用guava提供的Optional。我的优点很多。

答案 15 :(得分:0)

绝对是。

甚至JDK7解决了这个问题。见Objects#requireNonNull

void doWith(final Object mustBeNotNull) {

    /*
    // bush style
    if (mustBeNotNull == null) {
        throw new IllegalArgumentException("mustBeNotNull must not be null");
    }
    */

    /*
    // obama style
    if (mustBeNotNull == null) {
        throw new NullPointerException("mustBeNotNull must not be null");
    }
    */

    // kangnam style
    Objects.requireNonNull(mustBeNotNull, "mustBeNotNull must not be null");

    assert mustBeNotNull != null;
}

答案 16 :(得分:0)

答案 17 :(得分:0)

答案 18 :(得分:-1)