您的位置 首页 java

什么是JAVA中的NullPointerExceptions,如何避免?

什么是 java 中的空指针(NullPointerExceptions),如何避免程序中的空指针?

空指针异常可以说是java中最常见的异常之一了,你对它又了解多少呢?

什么是空指针

众所周知,JAVA中有两大类类型:基本类型和引用类型。基本类型可以由编译器保证使用前必须初始化为一个有效值(但是不一定是业务的有效值),使用它不会导致空指针异常;而引用类型可以被初始化为一个特殊的值 null ,说明 该引用未引用任何值
当使用一个 null 对象的属性,或调用其方法时,会触发空指针异常( NullPointerExceptions )。下面是官方文档给出的抛出异常的情况:

  • Calling the instance method of a null object.(调用 null 的实例方法)
  • Accessing or modifying the field of a null object.(调用或者修改 null 的属性)
  • Taking the length of null as if it were an array.(获取 null 数组的长度)
  • Accessing or modifying the slots of null as if it were an array.(访问或修改 null 数组中的元素)
  • Throwing null as if it were a Throwable value.(常规抛出null异常)

调用null的实例方法 / 调用或者修改null的属性

可以看下面的代码示例:

 // 类型定义
class SomeClass{
    // 仅作演示,尽量不要定义public的属性。
    public int someField;
    public  void  someMethod(){
    }
}

SomeClass someClass = null;
someClass.someMethod();
someClass.someField = 10;
  

输出:

  Exception  in thread "main" java.lang.NullPointerException
    ...
  

SomeClass someClass = null; 声明了一个引用 someClass 并赋值为 null someClass.someMethod(); 尝试调用 null 的实例方法(java8+也可能通过 someclass::somemethod 语法来调用),会抛出空指针异常; someClass.someField = 10; 修改 null 的属性,也会抛出空指针异常。

获取null数组的长度 / 访问或修改null数组中的元素

可以看下面的代码示例:

 int[] nullArray = null;
int length = nullArray.length;
nullArray[0] = 10;
  

输出:

 // 获取长度空指针
Exception in thread "main" java.lang.NullPointerException
    at ...

  

int[] nullArray = null; 声明了一个引用 nullArray 并赋值为 null int length = nullArray.length; 获取 null 数组的长度,会抛出空指针异常; nullArray[0] = 10; 修改 null 数组中的元素,也会抛出空指针异常。

常规抛出null异常

出了”被动”触发空指针异常外,我们还可以手动抛出空指针异常,比如这样: throw new NullPointerException(“null!”);

编程中一些空指针异常触发场景以及规避方法

方法参数中的空指针

方法参数中的空指针触发场景

当我们 对外开放 一个方法时,方法的入参就有可能会被调用方传入null,比如下列方法:

 int someMethod(Object someObj){
    someObj.xxx();
    return 0;
}
  

如果调用方传入null: int res = someMethod(null); ,那么在下面就会导致空指针异常了。

方法参数中的空指针防范方法

对于这类对外开放的(public/protected)方法来说,保护自己就很重要,防范方法一般是提前检查入参是否满足要求,如果不满足要求则抛出异常,可以通过类库中的 Objects.requireNonNull() 来完成,比如:

 int someMethod(Object someObj){
    Objects.requireNonNull(someObj, "someObj must not be null");
    someObj.xxx();
}
  

这一条当然不只适用于我们自己编写程序的时候,在使用第三方的 类库 时,也要注意使用的类库的方法参数是否支持 null ,对方也许粗心大意未处理这类异常,自己要多加小心。

方法返回值中的空指针异常

方法返回值空指针的触发场景

使用别人的程序时,除了要注意入参以外,返回值也要额外留意,确认该方法的返回值是否会返回null,防止造成不必要的麻烦。比如:

  Integer  getArrayLength(int[] num){
    if (num == null){
        return null;
    } else{
        return num.length;
    }
}
  

上述方法会返回传入的数组长度,当传入数组为空时,会返回null。如果调用时,传入了一个 null 数组,并且尝试用它返回的值进行加减,那么就会导致空指针异常了,如下所示:

 int[] array = null;
......很长的代码之后
Integer length = getArrayLength(array);
// 空指针!
int res = length + 10;
.....
  

这里的空指针看起来没有那么明显,实际上,在 getArrayLength 返回一个装箱类型 Integer 的时候,这个异常就埋下了伏笔,在 int res = length + 10; 时,返回的 length 被自动拆箱,导致了空指针异常。
这也是一种比较罕见的情况,但是出现这类问题可能会有漫长的debug等着我们了。
当返回值是装箱类型的时候,务必要特别留意。

除了上面这种以外,还有一类 obj.getXX().getXXX().. 的调用方法需要额外小心,一旦出现问题很难定位,最好别用。

方法返回值空指针的防范方法

对于方法的设计者来说,尽量不要返回 null ,返回数组和集合时,尽量返回一个空数组 new SomeObj[0] 或者是空集合 Collections.emptyXXX() ,如果是因为入参存在问题无法正常返回,及时抛出参数异常,不要默默返回 null ;遇到必须返回 null 的时候,可以返回 Optional<T> 。 尽量不要返回装箱类型,除非迫不得已。

对于方法的使用方来说,最好确定方法会不会返回 null 并做对应处理。实在无法确定的,最好自己判断一下空指针。遇到装箱返回值的,要警惕自动拆箱。

语句块中的空指针

这类空指针比较少见,语句块可以是for/switch/ synchronized 等。下面依次举例:

 // for语句块中,iterable为null会导致空指针
for (element : iterable) 
// switch语句块中,表达式xxx结果为空会导致空指针
switch (xxx) { ... }
// synchronized语句块中,传入null会导致空指针异常
synchronized (someNullReference) { ... }
  

防范方法主要是提前检查。

一些其他的防范方法

string 比较时,常量在前面

见代码:

 if ("some string".equals(xxx))
  

使用SonarLint插件(idea)

SonarLint插件不止可以检查空指针,还可以检查很多的常见编程问题。 idea 中可以直接在插件市场搜索安装。
提示: idea中代码被标注成背景是黄色时,通常说明可能存在问题,可以把鼠标挪上去查看详情。

jdk14中的空指针异常增强

jdk14中添加了对于空指针异常友好的提示,便于开发者快速定位空指针的对象。示例代码:

 int[] nullArray = null;
nullArray[0] = 10;
  

输出如下:

 Exception in thread "main" java.lang.NullPointerException: Cannot store to int array because "nullArray" is null
  

可以看到比之前的提示友好了很多,可以直接定位问题所在。

文章来源:智云一二三科技

文章标题:什么是JAVA中的NullPointerExceptions,如何避免?

文章地址:https://www.zhihuclub.com/200027.shtml

关于作者: 智云科技

热门文章

网站地图