摘要:我们应该考虑使用字符串常量调用方法来代替使用对象调用该方法。然而如果我们通过字符串常量来调用方法,执行流程会正常进行检查方法的参数在执行方法的方法体之前,务必对方法的参数进行值检查。
java.lang.NullPoinerException – 怎么处理空指针异常原文地址
作者 Sotirios-Efstathios (Stathis) Maneas
译者 smallclover
Thanks for your watching!
在java中,null是一个特殊的值,它能够被赋值给对象的引用。表示该对象的值不确定。当一个应用试图使用或者访问一个引用为null的对象时,一个NullPointerException 会被抛出。以下列举的几种情况将会抛出该异常:
通过一个 null 对象调用 方法
访问或者修改一个null 对象的字段
获取null的长度,比如,一个引用值为null的数组
访问或修改一个null对象,比如 一个引用值为null的数组。
抛出 NullPointException
当你试图同步一个null对象的时候。
NullPointerException是一个RuntimeException,并且javac编译器将不会强制你使用try-catch代码块处理NullPointerException。
为什么我们需要null值?正如前面所提到的那样,在java null值是一个特殊的值。Null值经常被使用在一些设计模式中,如Null Object Pattern 和 Singleton Pattern。前者,提供一个对象来代替当该类型的对象缺失的时候;后者确保该类有且仅有一个类的实例被创建。目的是为了提供一个该对象的全局唯一访问点。
例如,生成一个类的唯一实例的一般方法是:声明所有的构造函数为私有,然后创建一个公有的方法返回该类的唯一一个实例
TestSingleton.java:
01 import java.util.UUID; 02 03 class Singleton { 04 05 private static Singleton single = null; 06 private String ID = null; 07 08 private Singleton() { 09 /* Make it private, in order to prevent the creation of new instances of 10 * the Singleton class. */ 11 12 ID = UUID.randomUUID().toString(); // Create a random ID. 13 } 14 15 public static Singleton getInstance() { 16 if (single == null) 17 single = new Singleton(); 18 19 return single; 20 } 21 22 public String getID() { 23 return this.ID; 24 } 25 } 26 27 public class TestSingleton { 28 public static void main(String[] args) { 29 Singleton s = Singleton.getInstance(); 30 System.out.println(s.getID()); 31 } 32 }
在上面的这个例子中,我们声明了一个Singleton类的静态实例,该静态实例在getInstance内部执行最多一次的初始化。注意,这里使用null值,使得唯一的实例被创建。
怎样避免NullPointerException为了避免出现NullPointerException,在你使用该对象之前确保所有的对象被正常的初始化。注意,当你声明了一个引用变量,你就真的创建了一个指向对象的指针。在你通过这个对象请求方法或者属性时你必须确保这个指针不是null。
另外,如果该异常被抛出,请灵活的使用堆栈跟踪中的信息,通过JVM提供的堆栈跟踪,我们能够调试应用。查找exception发生的方法以及位于该方法的多少行,然后判断指定的那一行中哪一个引用等于null。在剩余的章节,我们将具体的描述一些处理上述异常情况的技术,然而它们也并不能完全消除NullPointerException的问题,所以程序员还是应该在编写应用的时候仔细一些。
1. String与字符串常量作比较
在一个应用中经常会编写涉及到比较String变量和字符串常量的代码。这个字符串常量可能是String类型或者是Enum的一个元素。我们应该考虑使用字符串常量调用equals方法来代替使用null对象调用该方法。例如观察一下案例:
1 String str = null; 2 if(str.equals("Test")) { 3 /* The code here will not be reached, as an exception will be thrown. */ 4 }
上面的代码片段将会抛出NullPointerException。然而如果我们通过字符串常量来调用equals方法,执行流程会正常进行:
1 String str = null; 2 if("Test".equals(str)) { 3 /* Correct use case. No exception will be thrown. */ 4 }
2. 检查方法的参数
在执行方法的方法体之前,务必对方法的参数进行null值检查。只有在在确保属性被检查之后再继续执行函数。另外,在传递的参数有错误的时候你可以抛出一个IllegalArgumentException通知调用者。
例如:
1 public static int getLength(String s) { 2 if (s == null) 3 throw new IllegalArgumentException("The argument cannot be null"); 4 5 return s.length(); 6 }
3. 使用String.valueOf()方法代替toString()
当你的应用代码需要获得一个对象的字符串的表示形式,请避免使用这个对象的toString方法,如果你的对象的引用等于null,将会导致NullPointerException被抛出。我们可以考虑使用静态的String.valueOf方法,该方法不会抛出任何异常,当函数的参数为null时会打印一个“null”字符串。
译注:这里只是一个建议,具体使用什么方法还是需要对应具体的生产环境
4. 使用三元运算符
三元运算符对于我们避开NullPointerException是非常有用的。该操作符格式如下:
1 boolean expression ? value1 : value2;
首先一个boolean表达式将会被判断,如果表达式为true,value1的值将会被返回,否则,value2的值会被返回。我们使用三元表达式处理空指针,如下图所示
1 String message = (str == null) ? "" : str.substring(0, 10);
在str的引用值为null的时候message变量的值为空,否则,如果str指向实际的数据,message将会获取它前10个字符。
5. 创建一个返回空的集合的方法来代替null。
一个非常好的技巧就是创建一个能返回空集合的方法来代替null值。你的应用代码能够迭代这个空集合并且可以使用它的方法和属性,而不用担心抛出NullPointerException。例如
Example.java 01 public class Example { 02 private static Listnumbers = null; 03 04 public static List getList() { 05 if (numbers == null) 06 return Collections.emptyList(); 07 else 08 return numbers; 09 } 10 }
6. 使用Apache的StringUtils类
apache的Commons Lang类库提供操作java.lang ApI的实用工具类,比如提供很多操作字符串的方法的类StringUtils.java,可以处理值为null的字符串。
你能使用诸如 StringUtils.isNotEmpty、StringUtils.IsEmpty和StringUtils.equals方法,为了减少NullPointerException。举个例子:
1 if (StringUtils.isNotEmpty(str)) { 2 System.out.println(str.toString()); 3 }
7. 使用contains(),containsKey(),containsValue()方法
如果你的应用使用集合,例如Maps,那你应该考虑使用contains(),containsKey(),containsValue()方法,在你确定value存在于map的时候你可以根据指定的key来获取值。
1 Mapmap = … 2 … 3 String key = … 4 String value = map.get(key); 5 System.out.println(value.toString()); // An exception will be thrown, if the value is null.
上面的代码片段,我们没有检查是否这个key存在于map中,所以可能会返回null值,最安全的做法如下述代码所示:
1 Mapmap = … 2 … 3 String key = … 4 if(map.containsKey(key)) { 5 String value = map.get(key); 6 System.out.println(value.toString()); // No exception will be thrown. 7 }
8. 检查外部方法的返回值
使用第三方库在我们的日常开发中是十分常见的。当这些libraries包含的方法返回引用的时候,确保方法返回的引用不是null。另外,应该认真的阅读方法的Javadoc,为了更好的理解它的功能以及它的返回值。
9. 使用断言
当你在测试代码时使用断言是十分有用的,为了避免执行的代码片段抛出NullPointerException。Java断言通过关键字assert来执行的并且在断言出错时会抛出AssertionError。
注意你必须明确的启用断言标志在JVM中,通过执行参数-ea.否则,断言将会被完全忽略。
下面代码是一个使用断言的例子:
1 public static int getLength(String s) { 2 /* Ensure that the String is not null. */ 3 assert (s != null); 4 5 return s.length(); 6 }
如果你执行以上的代码片段并且传递给getLength方法一个值为null的参数,然后会有如下的错误信息被打印出来:
1 Exception in thread "main" java.lang.AssertionError
10. 单元测试
当你在测试代码的功能性和正确性的时候单元测试是极其有用的。当你的应用代码出现特定的执行流程的时候,花一些时间写一些测试案例来验证该流程不会抛出NullPointerException。
Existing NullPointerException safe methods
1. 访问一个类的静态成员变量或者方法
当你的代码试图访问一个类的静态的变量和方法的时候,记事这个对象的引用为null,JVM也不会抛出NullPointerException。这是由于java编译器会在特别的地方存储静态的变量和字段。因此,静态字段和静态方法并不会关联到对象,而是与类的名字有关联。
例如,下述代码不会抛出NullPointerException。
TestStatic.java :
01 class SampleClass { 02 03 public static void printMessage() { 04 System.out.println("Hello from Java Code Geeks!"); 05 } 06 } 07 08 public class TestStatic { 09 public static void main(String[] args) { 10 SampleClass sc = null; 11 sc.printMessage(); 12 } 13 }
注意,尽管SampleClass对象的引用值为null,但是方法还是将被正确的执行。然而,当我们提到静态方法和静态字段时,最好的访问方式仍然是静态的访问方式(通过类名来访问),如SampleClass.printMessage().
2. Instanceof 操作
即使对象的引用值为null,instanceof操作也能被使用。Instanceof操作在引用的值为null的时候会返回false并且不会抛出NullPointerException。请思考一下代码片段:
1 String str = null; 2 if(str instanceof String) 3 System.out.println("It"s an instance of the String class!"); 4 else 5 System.out.println("Not an instance of the String class!");
执行的结果不出所料:
1 Not an instance of the String class!
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/66078.html
摘要:本文探讨使用构建集成的可执行程序的方法,以及根节点问题。而使用后,可指导类作为根节点,避免了嵌套的情况。文件设计如下文件同时指明了根节点的类型,资源文件对应的设计如下此时可实现开始时,纯代码方式的自定义控件设计。 「博客搬家」 原地址: 简书 原发表时间: 2017-05-22 上一篇文章探讨了使用 IntelliJ IDEA 创建 JavaFX 工程,进而开发了所需应用程序。更...
摘要:关于异常处理的文章已有相当的篇幅,本文简单总结了的异常处理机制,并结合代码分析了一些异常处理的最佳实践,对异常的性能开销进行了简单分析。是程序正常运行中,可以预料的意外情况,应该被捕获并进行相应处理。 关于异常处理的文章已有相当的篇幅,本文简单总结了Java的异常处理机制,并结合代码分析了一些异常处理的最佳实践,对异常的性能开销进行了简单分析。博客另一篇文章《[译]Java异常处理的最...
摘要:最近在写运维开发时经常碰见一些常见的文件的文件操作。将字符串写入文件中读取文件字符串文件不存在把文件转为数组文件不存在从数据库读取数据存入文件数据库操作将数组写入文件调用时只需要把文件包含进来就可以 最近在写运维开发时,经常碰见一些常见的文件的文件操作。特别在处理高并发的需求时,需要REDIS DOCUMENT DB同时操作,如果业务人员在文件处理上花费太多的时间会降低开发效率,因此笔...
摘要:指示该错误是否严重,此属性会在该异常根据错误的上下文遍历堆栈时进行更新,严重性会指示异常捕获代码是应该停止程序还是该继续处理。引发异常在检测到错误并无法从中恢复时,异常将向上传播到调用堆栈,直到到达处理它的某个块。 翻译:疯狂的技术宅 原文标题:Exception handling strategy 原文链接:http://programmergate.com/exc...本文首发微信...
摘要:的处理流程就是在树中的传递的过程这个过程分为步第一步,在树中寻找处理的第二步,剩余的在树传递给目标第一步,在树中寻找处理的递归方式完成从顶向下传递,找到到的最底层的从底向上,查找可以处理的并记录从到的路径其中涉及到以及递归的方式调用的寻找最 TouchEvent的处理流程就是TouchEvent在View树中的传递的过程:这个过程分为2步:第一步,ACTION_DOWN在View树中寻...
阅读 3013·2021-11-24 10:47
阅读 3799·2021-11-02 14:43
阅读 2207·2021-09-26 10:15
阅读 2180·2021-09-08 09:35
阅读 536·2019-08-30 12:45
阅读 2758·2019-08-29 17:04
阅读 3199·2019-08-26 14:05
阅读 1240·2019-08-26 12:10