避免 Java 应用中 NullPointerException 的技巧和最佳实践

Java应用中抛出空指针异常是解决空指针的最好方式,也是写出能顺利工做的健壮程序的关键。熟话说“预防胜于治疗”,对于这么使人讨厌的空指针异常,这句话也是成立的。值得庆幸的是运用一些防护性的编码技巧,跟踪应用中多个部分之间的联系,你能够将Java中的空指针异常控制在一个很好的水平上。顺便说一句,这是Javarevisited上的第二个空指针异常的帖子。在上个帖子中咱们讨论了Java中致使空指针异常的常见缘由,而在本教程中咱们将会学习一些Java的编程技巧和最佳实践。这些技巧能够帮助你避免Java中的空指针异常。听从这些技巧一样能够减小Java代码中处处都有的非空检查的数量。做为一个有经验的Java程序员,你可能已经知道其中的一部分技巧而且应用在你的项目中。但对于新手和中级开发人员来讲,这将是很值得学习的。顺便说一句,若是你知道其它的避免空指针异常和减小空指针检查的Java技巧,请和咱们分享。 html

这些都是简单的技巧,很容易应用,可是对代码质量和健壮性有显著影响。根据个人经验,只有第一个技巧能够显著改善代码质量。如我以前所讲,若是你知道任何避免空指针异常和减小空指针检查的Java技巧,你能够经过评论本文来和分享。 java

1) 从已知的String对象中调用equals()和equalsIgnoreCase()方法,而非未知对象。

老是从已知的非空String对象中调用equals()方法。由于equals()方法是对称的,调用a.equals(b)和调用b.equals(a)是彻底相同的,这也是为何程序员对于对象a和b这么不上心。若是调用者是空指针,这种调用可能致使一个空指针异常
Object unknownObject = null;
 
//错误方式 – 可能致使 NullPointerException
if(unknownObject.equals("knownObject")){
   System.err.println("This may result in NullPointerException if unknownObject is null");
}
 
//正确方式 - 即使 unknownObject是null也能避免NullPointerException
if("knownObject".equals(unknownObject)){
    System.err.println("better coding avoided NullPointerException");
}
这是避免空指针异常最简单的Java技巧,但可以致使巨大的改进,由于equals()是一个常见方法。

2) 当valueOf()和toString()返回相同的结果时,宁愿使用前者。

由于调用null对象的toString()会抛出空指针异常,若是咱们可以使用valueOf()得到相同的值,那宁愿使用valueOf(),传递一个null给valueOf()将会返回“null”,尤为是在那些包装类,像Integer、Float、Double和BigDecimal。
BigDecimal bd = getPrice();
System.out.println(String.valueOf(bd)); //不会 NPE,但要注意和 String.valueOf(null) 的区别
System.out.println(bd.toString()); //抛出 "Exception in thread "main" java.lang.NullPointerException"

3) 使用null安全的方法和库 

有不少开源库已经为您作了繁重的空指针检查工做。其中最经常使用的一个的是Apache commons 中的StringUtils。你可使用StringUtils.isBlank(),isNumeric(),isWhiteSpace()以及其余的工具方法而不用担忧空指针异常。 程序员

//StringUtils方法是空指针安全的,他们不会抛出空指针异常
System.out.println(StringUtils.isEmpty(null));
System.out.println(StringUtils.isBlank(null));
System.out.println(StringUtils.isNumeric(null));
System.out.println(StringUtils.isAllUpperCase(null));
 
Output:
true
true
false
false
可是在作出结论以前,不要忘记阅读空指针方法的类的文档。这是另外一个不须要下大功夫就能获得很大改进的Java最佳实践。

(PS:也能够考虑 google 的 Guava 库:com.google.common.base.Preconditions.checkNotNull
com.google.common.base.Optional)
数据库

4) 避免从方法中返回空指针,而是返回空collection或者空数组。

这个Java最佳实践或技巧由Joshua Bloch在他的书Effective Java中提到。这是另一个能够更好的使用Java编程的技巧。 经过返回一个空collection或者空数组,你能够确保在调用如size(),length()的时候不会由于空指针异常崩溃。 Collections类提供了方便的空List,Set和Map: Collections.EMPTY_LIST,Collections.EMPTY_SET,Collections.EMPTY_MAP。这里是实例。
public List getOrders(Customer customer){
    List result = Collections.EMPTY_LIST;
    return result;
}
你一样可使用Collections.EMPTY_SET和Collections.EMPTY_MAP来代替空指针。

5) 使用annotation@NotNull 和 @Nullable

在写程序的时候你能够定义是否可为空指针。经过使用像@NotNull和@Nullable之类的annotation来声明一个方法是不是空指针安全的。现代的编译器、IDE或者工具能够读此annotation并帮你添加忘记的空指针检查,或者向你提示出没必要要的乱七八糟的空指针检查。IntelliJ和findbugs已经支持了这些annotation。这些annotation一样是JSR 305的一部分,但即使IDE或工具中没有,这个annotation自己能够做为文档。看到@NotNull和@Nullable,程序员本身能够决定是否作空指针检查。顺便说一句,这个技巧对Java程序员来讲相对比较新,要采用须要一段时间。

6) 避免你的代码中没必要要的自动装箱(autoboxing)和拆箱(unboxing)。

且无论其余如建立临时对象的缺点,若是wrapper类对象是null,自动装箱一样容易致使空指针异常。例如若是person对象没有电话号码的话会返回null,以下代码会由于空指针异常崩溃。
Person ram = new Person("ram"); 
int phone = ram.getPhone(); // if ram.getPhone() == null, here will thown NPE.
当使用自动装箱和自动拆箱的时候,不只仅是等号,< 或者 > 一样会抛出空指针异常。你能够经过这篇文章来学习更多的Java中的自动装箱和拆箱的陷阱

(PS:因此尽可能避免返回包装类型,返回原始类型就能避免此类 NPE 问题了,或者返回类型与接收类型一致)
编程

7) 听从Contract并定义合理的默认值。

在Java中避免空指针异常的一个最好的方法是简单的定义contract并听从它们。大部分空指针异常的出现是由于使用不完整的信息建立对象或者未提供全部的依赖项。若是你不容许建立不完整的对象并优雅地拒绝这些请求,你能够在接下来的工做者预防大量的空指针异常。相似的,若是对象容许建立,你须要给他们定义一个合理的默认值。例如一个Employee对象不能在建立的时候没有id和name,可是是否有电话号码是可选的。如今若是Employee没有电话号码,你能够返回一个默认值(例如0)来代替返回null。可是必须谨慎选择,哟有时候检查空指针比调用无效号码要方便。一样的,经过定义什么能够是null,什么不能为null,调用者能够做出明智的决定。failing fast 或接受 null 一样是一个你须要进行选择并贯彻的,重要的设计决策。

8)定义数据库中的字段是否可为空。

若是你在使用数据库来保存你的域名对象,如Customers,Orders 等,你须要在数据库自己定义是否为空的约束。由于数据库会从不少代码中获取数据,数据库中有是否为空的检查能够确保你的数据健全。在数据空中维护null约束一样能够帮助你减小Java代码中的空指针检查。当从数据库中加载一个对象是你会明确,哪些字段是能够为null的,而哪些不能,这可使你代码中没必要要的!= null检查最少化。

9) 使用空对象模式(Null Object Pattern)

还有一种方法来避免Java中的空指针异常。若是一个方法返回对象,在调用者中执行一些操做,例如Collection.iterator()方法返回迭代器,其调用者执行遍历。假设若是一个调用者并无任何迭代器,其能够返回空对象(Null object)而非null。空对象是一个特殊的对象,其在不一样的上下文中有不一样的意义。例如一个空的迭代器调用hasNext()返回false时,能够是一个空对象。一样的在返回Container和Collection类型方法的例子中,空对象能够被用来代替null做为返回值。我打算另写一篇文章来说空对象模式,分享几个Java空对象的例子。 数组

以上就是所有内容了,这是几个易于听从的避免空指针异常的Java技巧和最佳实践。你应该能体会到这些技巧将很是有用,且不太难实现。若是你有其余处理/预防空指针异常的技巧,而又没包含在这里,请经过评论来和咱们分享,我将收录在这里。

10) Refer:

http://javarevisited.blogspot.com/2013/05/ava-tips-and-best-practices-to-avoid-nullpointerexception-program-application.html 安全

相关文章
相关标签/搜索