如何处理空指针以及什么是“空安全”的代码方式?

时间:2016-11-03 02:20:53

标签: java null

我是初级开发人员,我使用java进行网站开发。 我知道org.apache.common.lang.StringUtils因为它的null安全性而被推荐。 但什么是null安全或null安全确切? 以下代码为何如此丑陋?

  

if(sth!= null){   ...   }

4 个答案:

答案 0 :(得分:1)

空指针可以说是最常见的运行时崩溃源 - 它们实际上是时间炸弹。 java中的可怕空值检查被大多数高级开发人员认为是代码味道,通常是设计不良的标志。

更安全的方法是使用Null对象模式作为zhc提到。在这种情况下,无论何时声明它,都要创建一个未初始化的对象,而不是让它在填充之前保持为空。

这个过于简化的例子是始终用“”实例化一个String。一个空标签(在我看来)比崩溃更可取。

答案 1 :(得分:1)

对于初级到中级程序员来说,这是最常见的问题:他们要么不知道,要么不信任他们参与的合同,并且在防范方面检查是否为空。

为什么以下代码很难看?

 if( sth != null ) { ... }

它并不像你所知的那么难看,但如果我们在项目中有很多null检查条件,我们认为它是额外检查而不是可读代码。 (对于这种情况,如果您接受null是合同方面的有效回复;以及......)

但什么是null安全或null安全确切?
 以下是我对" null安全"的建议。根据我经验丰富和最喜欢的作者编写代码的方式。

For Collection Object Case

(1)返回空数组或集合,而不是空值(Effective Java(参见第43项) - Joshua Bloch)

// The right way to return an array from a collection
private final List<Cheese> cheesesInStock = ...;

private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0];
/**
* @return an array containing all of the cheeses in the shop.
*/
public Cheese[] getCheeses() {
     return cheesesInStock.toArray(EMPTY_CHEESE_ARRAY);
}

以类似的方式,可以使用集合值方法返回相同的方法 每次需要返回空集合时,不可变空集合。 Collections.emptySetemptyListemptyMapmethods 完全符合您的需求,如下所示:

// The right way to return a copy of a collection
public List<Cheese> getCheeseList() {
    if (cheesesInStock.isEmpty())
     return Collections.emptyList(); // Always returns same list
   else
     return new ArrayList<Cheese>(cheesesInStock);
}

总之,没有理由从[{1}} - 或null返回array collection - 值方法,而不是返回空数组或集合。

(2)不要退回 - (清洁代码 - 鲍勃叔叔)
在许多情况下,特殊情况对象是一种简单的补救措施。想象一下,你有这样的代码:

List<Employee> employees = getEmployees();
if (employees != null) {
  for(Employee e : employees) {
   totalPay += e.getPay();
  }
}

现在,getEmployees可以返回null,但它必须返回吗?如果我们更改getEmployeeso它返回一个空列表,我们可以清理代码:

List<Employee> employees = getEmployees();
for(Employee e : employees) {
  totalPay += e.getPay();
}

幸运的是,JavaCollections.emptyList(),它返回一个我们可以用于此目的的预定义不可变列表:

public List<Employee> getEmployees() {
   if( .. there are no employees .. ) 
     return Collections.emptyList();
}

如果您采用这种方式编码,则可以最大限度地减少NullPointerExceptions的可能性,并使您的代码更清晰。

不要通过
从方法返回null很糟糕,但将null传递给方法会更糟糕。除非您使用的是希望通过null API ,否则应尽可能避免在代码中传递null

  让我们看一个例子,看看为什么。这是一个计算两点度量的简单方法:

public class MetricsCalculator 
{
    public double xProjection(Point p1, Point p2) {
        return (p2.x – p1.x) * 1.5;
    }
…
}

当有人将null作为参数传递时会发生什么?

calculator.xProjection(null, new Point(12, 13));

当然,我们会得到NullPointerException

我们如何解决这个问题?我们可以创建一个新的异常类型并抛出它:

public class MetricsCalculator 
{
    public double xProjection(Point p1, Point p2) {
        if (p1 == null || p2 == null) {
        throw InvalidArgumentException(
        "Invalid argument for MetricsCalculator.xProjection");
        }
        return (p2.x – p1.x) * 1.5;
    }
}


这是否更好?它可能比nullpointerexception好一点,但请记住,我们必须为InvalidArgumentException定义一个处理程序。处理程序应该做什么?有什么好的行动方案吗?

还有另一种选择。我们可以使用一组断言:

public class MetricsCalculator 
    {
        public double xProjection(Point p1, Point p2) {
        assert p1 != null : "p1 should not be null";
        assert p2 != null : "p2 should not be null";
        return (p2.x – p1.x) * 1.5;
    }
}

这是很好的文档,但它没有解决问题。如果有人传递null,我们仍会遇到runtime错误。
在大多数编程语言中,没有好的方法来处理null 不小心通过来电者。因为这种情况,理性的方法是默认情况下禁止传递null 。当你这样做时,你可以编码,知道参数列表中的null是一个问题的指示,并最终减少了粗心的错误。

额外注意: null - 返回惯用语很可能是 C编程语言的延续,其中数组长度与实际数组分开返回。在 C 中,如果返回零作为长度,则分配数组没有任何优势。

对于非收集对象案例

(1)使用Null Object Pattern (旧方法)
例如(假设您使用dao模式访问db)
您需要做的就是返回一个空对象 - 比如您在 DAO 中的客户条目......

if (result == null) { return new EmptyUser(); }

其中EmptyUser扩展User并将适当的条目返回到getter调用,以允许其余代码知道它是一个空对象(id = -1等) 代码示例:

public class User {
    private int id;
    private String name;
    private String gender;
    public String getName() {
       //Code here
    }
    public void setName() {
       //Code here
    }
}

public class EmptyUser extends User {

    public int getId() {
       return -1;
    }

    public String getName() {
       return String.Empty();
   }
}

public User getEntry() {
   User result = db.query("select from users where id = 1");
   if(result == null) {
       return new EmptyUser();
   }
   else {
       return result;
    }
}

(2)使用Java 8可选
确实引入null引用可能是编程语言中最糟糕的错误之一。历史甚至它的创造者Tony Hoare称之为十亿美元的错误。

根据新的null版本,以下是Java的最佳替代方法:

<强> 2.1。 Java 8及以上

Java 8开始,您可以使用java.util.Optional.

以下是如何在非null返回情况下使用它的示例:

public Optional<MyEntity> findMyEntity() {
    MyEntity entity = // some query here
    return Optional.ofNullable(entity);
}


2.2。 Java 8之前

Java 8之前,您可以使用Google Guava中的com.google.common.base.Optional

以下是如何在非null返回情况下使用它的示例:

public Optional<MyEntity> findMyEntity() {
    MyEntity entity = // some query here
    return Optional.fromNullable(entity);
}


注意空对象模式V Java 8 Optional

我明确地更喜欢Optional更多generic并且已经被甲骨文和Google,采用了全球最大的两家IT公司对它的信用。

我甚至会说{strong>空对象模式在Java中没有任何意义,它是过时的Optional是未来如果你仔细检查一下Java 9中的新功能,你会看到 Oracle Optional更进一步,阅读{ {3}}

答案 2 :(得分:1)

  

我知道推荐使用org.apache.common.lang.StringUtils,因为它的安全性为零。

通常使用String值,对于最基本的字符串操作,代码将充满if (foo != null) {...

Apache写了很多Java code所以它已经竭尽全力为常见操作提供辅助工具。

例如:

int i = s.indexOf('foo');    // Can throw NullPointerException!
String b = s.toUpperCase();  // Can throw NullPointerException!!
if (s.equals('bar')          // Can throw NullPointerException!!!
   // ...

如果您开始阅读StringUtils课程,您会注意到null一遍又一遍地处理indexOfupperCaseequals - 全部涵盖null安全。这可以大大简化您的代码。

来自apache.org:

  

“StringUtils静默处理空输入字符串。也就是说,null输入将返回null。返回布尔值或int的详细信息因方法而异”

根据我的经验,使用StringUtils的决定直接受项目中使用的if (foo == null) { ...语句数量的影响,以及您的代码中的依赖项是否已导入此特定实用程序/ library - 所有这些都可能在项目的整个生命周期内发生变化。

答案 3 :(得分:0)

使用空检查的代码并不难看。例如,如果您使用C语言或Go语言检查某些代码,则它们会充满空检查。 另外,关于你的“无效安全”方式。我认为这是使用GoF的Null Object模式。