Java避免NullPointerException的一些技巧

 

那些情况会引发该异常呢?

  • 被调用方法的对象为null。

  • 访问或修改一个null对象的字段。

  • 求一个数组为null对象的长度。

  • 访问或修改一个数组为null对象中的某一个值。

  • 被抛出的值是null并且是一个Throwable的子类。

  • 当你用null对象进行synchronized代码块。

NullPointerException 是 RuntimeException 的子类,因此,Javac 编译器并不会强迫你使用 try-catch 代码块来捕获该异常。

一、为什么需要 null ?

如上所述,null 是 Java 的一个特殊值。它在设计模式方面编码的过程中非常有用,例如空对象模式和单例模式。空对象模式提供了一个对象作为缺少给定类型对象的代理。而单例模式可以确保只创建一个类的实例,主要用于提供一个全局访问的对象。

例如,创建单例类的示例方法是将其所有构造函数声明为private,然后创建一个返回该类的唯一实例的公共方法,如下:

import java.util.UUID;

public class TestSingleton {
	

	public static void main(String[] args) {
		
		Singleton s=Singleton.getInstance();
		System.out.println(s.getID());
	}
}

class Singleton{
	private static Singleton singleton = null;
	private String Id = null;
	
	private Singleton() {
		
		Id=UUID.randomUUID().toString();
	}
	
	public static Singleton getInstance() {
		
		if(singleton == null) {
			singleton = new Singleton();
		}
		return singleton;
	}
	
	public String getID() {
		
		return this.Id;
	}
}

在这个例子中,我们声明了一个 Singleton 类的静态实例。该实例在 getInstance 方法内最多初始化一次。注意本例使用了 null 来确保创建的是唯一实例。

二、如何避免空指针异常

为了避免这种情况 NullPointerException ,请确保在使用运行程序之前,所有对象都已正确初始化。注意,当你声明一个引用变量时,即创建了一个指向对象的指针。在向对象请求方法或字段之前,您必须验证变量「指针」是否为空。

另外,如果引发异常,请使用堆栈中的异常信息进行跟踪。堆栈跟踪是由JVM提供,便于应用程序的调试。找到发生异常的方法和代码行,然后确定哪个引用为null。

在本文的余下部分,我们将介绍一些避免空指针异常的方法。但是,这些方法并非一劳永逸,因此,同学们在编写应用程序时应格外小心。

1、String变量与文本值比较

在编码过程中,String变量与文本值之间的比较是特别常见的。一般被比较的值可以是一个字符串或枚举值。因此,我们不要从空对象调用方法进行比较,而应考虑从文字值中调用方法。如下:

                          

上面的代码片段则会抛出一个NullPointerException。但是,如果我们从文字中调用方法,那么执行流程通常会继续:

为什么呢?去看一下 JDK 源码 java.lang.String 便明白了。

    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

 

2、检查方法的参数

在执行你自己的方法的主体之前,一定要检查方法传入的参数是否为空。只有在正确检查了参数后,才能继续执行该方法的相应逻辑。否则,您可以抛出一个 IllegalArgumentException 来通知调用方法所传递的参数有问题。

例如:

3、优先使用String.valueOf() 代替toString()

当您代码中的某个对象需要用字符串的方式来表示时,请避免使用该对象的toString方法;因为若你的对象引用为null,则会抛出 NullPointerException。

相反,考虑使用静态String.valueOf方法,该方法不会抛出任何异常,若对象引用为空,则打印「null」字符串。

有的同学可能会问为什么呢?还是那句话读源码 ^_^

下面是基类 Object 的 toString() 方法:

public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

接着,咱们再来看看 String 的 valueOf() 方法做了什么呢?

public static String valueOf(Object obj) {
        return (obj == null) ? "null" : obj.toString();
    }

4、使用三元运算符

该操作是非常有用的,可以帮助我们避免了NullPointerException。格式如下

boolean  expersion ? value1 : value2

上面通过布尔表达式来判断。如果表达式结果为true,则返回value1,否则返回value2。我们可以使用三元运算符来处理空指针,如下所示: 

String message = ( str == null) ? " " : str.substring( 0,10 );

如果str的引用为空,则消息变量将为空。否则,如果str指向实际数据,则该消息将保留它的前10个字符。

这里,一直有个疑惑困扰着我,为什么 Kotlin 这门语言去掉了三元运算符呢?知道的同学欢迎留言,一起来探讨~~~

5、创建返回空集合而不是null值的方法

一个非常好的操作是创建返回一个空集合的方法,而不是一个null值。因为你的代码可以遍历空集合并使用它的方法和字段,而不会抛出一个NullPointerException 。例如:

public class Example {
        public static List<Integer> number = null;

	    public static List<Integer> getList(){
		if(number == null) 
			return Collections.emptyList();
		else
			return number;
	} 
}

注意:要熟悉 Collections 这个集合工具类,里面有太多好用的方法了。

6、使用Apache的StringUtils类

Apache的Commons Lang是一个为 java.lang API 提供帮助工具的库,比如字符串操作方法。提供字符串操作的示例类是 StringUtils.java,它对输入的字符串进行了 null 判断。

你可以使用 StringUtils.isNotEmpty,  StringUtils.IsEmpty 和 StringUtils.equals 等方法,来避免NullPointerException。例如:

if(StringUtils.isNotEmpty(str)){
    System.out.println(str.toString)
}

 

7、习惯用 contains(), containsKey(), containsValue() 方法

如果您的程序在使用集合,请考虑使用contains,containsKey和containsValue方法。例如,从集合中找一个特定键的值:

Map <String,String>  map=
....
String key = ...
String value = map.get(key);
System.out.println (value.toString());

 在上面的代码片段中,我们未检查key是否真的存在于内部Map,因此返回的值可以是 null 。最安全的方法如下:

Map <String,String>  map=
....
String key = ...
if(map.containsKey(key)){
    String value = map.get(key);
    System.out.println (value.toString());
}

8、请检查使用的外部方法的返回值是否为 null

在编码中使用外部库是很常见的,这些库可能包含返回引用的方法,需确保返回的值不为 null 。另外,我们要养成在开发的过程中,养成阅读 Javadoc 的习惯,以便更好地理解其功能和返回值。

9、使用断言

断言在测试代码时非常有用,并且可以被使用,以避免 NullPointerException 。Java 断言是用 assert 关键字实现的,并抛出一个 AssertionError 。

请注意,您必须显式启用 JVM 的断言标志,一般在程序启动时,使用 –ea 参数来启用断言。否则,断言将被完全忽略。

使用 Java 断言的示例如下:

public static int getLengths(String s) {
		assert(s != null);
		return s.length();
	}

 如果您执行上面的代码段并传递一个空参数getLength,则会出现以下错误消息:

Exception in thread "main" java.lang.NullPointerException

最后,您可以使用测试框架  JUnit 提供的类 Assert 来使用断言。

10、单元测试

在测试代码的功能和正确性时,单元测试一般非常有用。因此,建议多花一些时间编写一些测试用例,来避免程序出现 NullPointerException。目前,我司的代码覆盖率要达到 95% 以上才能通过。

三、拥有 NullPointerException 的安全方法

1、访问类的静态成员或方法

当你的代码试图访问静态变量或类的方法时,即使对象的引用等于 null,JVM 也不会抛出一个 NullPointerException 。这是由于Java编译器在编译过程中将静态方法和字段存储在方法区或者常量池。因此,静态字段和方法不与对象相关联,而与类的名称相关联。

例如,下面的代码不会抛出NullPointerException:

public class Test {

	public static void main(String[] args) {
		SimpleClass s = null;
		s.printMessage();
		
	}
}

class SimpleClass{
	public static void printMessage() {
		System.out.println("Hello My New World");
	}
}

注意,尽管 SampleClass 等于的实例 null 将会被正确执行。但是,对于静态方法或字段,最好以静态方式访问它们,比如SampleClass.printMessage()。

2、instanceof 操作符

instanceof 即使对象的引用等于 null,也可以使用该运算符。在 instanceof 操作时,参考值等于为null,不抛出 NullPointerException,而是返回 false 。例如,下面的代码片段:

String str = null;
if( str instanceof String) 
	System.out.println("It's an instance of the String class");
else 
	System.out.println("Not an instance of the String class");

正如预期的那样,执行的结果是:

Not an instance of the String class

注:转载自别人的文章,向作者致敬

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值