关于编码风格:Java Null参考最佳实践

Java Null Reference Best Practice

在Java中,下面哪一个是处理可能的空引用的更"被接受"的方式?请注意,空引用并不总是指示错误…

1
2
3
4
5
6
if (reference == null) {
    //create new reference or whatever
}
else {
    //do stuff here
}

1
2
3
4
5
6
try {
    //do stuff here
}
catch (NullPointerException e) {
    //create new reference or whatever
}

已经给出的答案非常好(不要将异常用于控制流;异常的抛出和处理成本很高)。还有另外一个重要的原因,特别是没有抓到NullPointerException

考虑执行以下操作的代码块:

1
2
3
4
5
6
7
try {
    reference.someMethod();
    // Some other code
}
catch (NullPointerException e) {
    // 'reference' was null, right? Not so fast...
}

这似乎是处理reference无效性的一种安全方法……但是,如果引用是非空的并且someMethod()引发了NPE呢?或者,如果在try区块的其他地方有一个NPE上升呢?捕获NPE是防止发现和修复错误的可靠方法。


捕获异常是相对昂贵的。通常情况下,最好是检测情况,而不是对其作出反应。


当然是这个

1
2
3
4
5
6
if (reference == null) {
    //create new reference or whatever
}
else {
    //do stuff here
}

我们不应该依赖例外来做决定,因为这根本不是为了这个目的,而且它们也很昂贵。

如果你不做决定,只是验证初始化变量,那么

1
2
3
4
if (reference == null) {
    //create new reference or whatever
}
//use this variable now safely

我见过一些自动代码生成器用accessors/getter方法包装这件事。


从答案中可以清楚地看出,捕获异常是不好的。:)例外绝对不是免费的。这可能有助于你深入理解它。.

我还想提到另一种做法,同时比较你的对象和已知的值。这是做这项工作的传统方法:(检查对象是否为空,然后比较)

1
2
3
4
Object obj = ??? //We dont know whether its null or not.
if(obj!=null && obj.equals(Constants.SOME_CONSTANT)){
    //your logic
}

但是这样,你就不必为你的目标烦恼了:

1
2
3
4
Object obj = ???
if(Constants.SOME_CONSTANT.equals(obj)){  //this will never throw
                                          //nullpointer as constant can not be null.
}

我认为一般情况下,应该为异常情况保留一个异常——如果有时需要空引用,则应该检查并显式处理它。


第一种形式:

1
2
3
4
5
6
7
8
if (reference == null)
{    
    //create new reference or whatever
}
else
{    
    //do stuff here
}

不应对控制流使用异常。

例外情况是处理在正常操作条件下通常不会发生的例外情况。


第一,抛出异常是一个代价高昂的操作。


既然您要求最佳实践,我想指出MartinFowler建议将空引用的子类作为最佳实践引入。

1
public class NullCustomer extends Customer {}

因此,您可以避免处理nullPointerException的麻烦,这是未经检查的。方法可能返回客户值为空,然后将返回空客户而不是空客户。

您的支票如下:

1
2
3
4
5
6
7
final Customer c = findCustomerById( id );
if ( c instanceof NullCustomer ) {
    // customer not found, do something ...
} else {
    // normal customer treatment
    printCustomer( c );
}

在我看来,在某些情况下,可以捕获NullPointerException,以避免对空引用进行复杂的检查,并增强代码的可读性,例如。

1
2
3
4
5
6
private void printCustomer( final Customer c ) {
try {
    System.out.println("Customer" + c.getSurname() +"" + c.getName() +"living in" +     c.getAddress().getCity() +"," + c.getAddress().getStreet() );
} catch ( NullPointerException ex ) {
    System.err.println("Unable to print out customer information.", ex );
}

反对它的一个论点是,通过检查单个成员是否为空,您可以编写更详细的错误消息,但这通常不是必需的。


这与您的开发风格有关,如果您使用"安全"风格开发代码,则必须使用

1
2
3
4
5
    if(null == myInstance){
// some code    
    }else{
// some code
    }

但是,如果您不使用这种样式,那么至少应该捕获异常,但在这种情况下,它是nullpointerException,我认为最好检查输入参数是否为空,而不是等待抛出异常。


在这种情况下,当我们可以开始做的时候,尝试捕捉方法可能会变得有意义。

1
2
3
4
5
6
7
try {
  //do stuff here
}
catch (NullPointerException e) {
  //create new reference or whatever
  retry;
}


您应该在不希望出现错误的地方使用异常捕获。如果某个值可以为空,那么您应该检查它。