Optional类与空指针(上)

简介: 当程序需要对象实例的时候返回null就会抛出空指针异常(NullPointerException,简称NPE)。包括以下情况:调用一个null对象实例的方法访问或修饰null对象的字段获取数组为null时的长度访问或修饰数组为null时的索引值抛出Throwable对象为null时的异常虽然代码很难万无一失地避免所有NPE,但是也要尽量减少。所以一些防御性的编程技巧,可以将NPE控制在一个很好的水平上。


一、什么是空指针异常



当程序需要对象实例的时候返回null就会抛出空指针异常(NullPointerException,简称NPE)。包括以下情况:

  • 调用一个null对象实例的方法
  • 访问或修饰null对象的字段
  • 获取数组为null时的长度
  • 访问或修饰数组为null时的索引值
  • 抛出Throwable对象为null时的异常

虽然代码很难万无一失地避免所有NPE,但是也要尽量减少。所以一些防御性的编程技巧,可以将NPE控制在一个很好的水平上。


空指针案例


1. 调用业务方法的返回值对象

在不清楚一个方法的返回值是否存在返回null的情况,直接使用对象返回值。

People people = new People();
People user = people.getUser("name");
String name = user.getName();
System.out.println(name);

上面示例中的people.getUser("name");调用返回的对象不清楚是否为null,后面直接调用该对象的方法造成NPE。


2. 包装类自动拆箱的值

在包装类对象的值为null的情况下,进行自动拆箱操作。

Integer a = null;
int b = a;
// System.out.println(1 == a);

上面示例中包装类对象a定义时的初始化值为null,在将a赋值给基本数据类型的b的时候,以及与基本数据类型1进行相等逻辑操作的时候,都进行了自动拆箱操作,anull时造成NPE。


3. 集合和数组空对象的遍历

在不清楚集合或数组是否为null的时候,对它们进行遍历操作。

List<String> list = null;
// String[] list = null;
for (String string : list) {
   System.out.println(string);
}

在方法返回或者自己定义的数组和集合,只要有null的情况(不包括数组和集合长度为0的情况),进行遍历操作时造成NPE。


4. Spring没注入实例的使用

在使用Spring框架时,如果注入对象实例失败,此时该对象也是null

public class BeanExample {
    @Autowired
    private BeanProvider beanProvider;
    public void run() {
        this.beanProvider.sayHello(this.name, this.age);
    }
}

当因为操作不当导致beanProvider没有注入,在调用sayHello()方法的时候造成NPE。


5. ConcurrentHashMap和Hashtable的值

对某些不支持null值的集合添加null值元素,比如ConcurrentHashMapHashtable

ConcurrentHashMap<String, String> map = new ConcurrentHashMap();
map.put("a", null);
Hashtable<String, String> hashtable = newHashtable<>();
hashtable.put("a", null);

这些集合的低层put(K key,V value)方法中,在key或者valuenull的情况下造成NPE。


二、怎样防止空指针异常



既然NPE难以避免,我们就要去找各种方法来解决。既要有良好的编码习惯,也要细心的去把控业务。


1. 普通处理

在针对调用业务方法进行NPE普通地防御,可以简单的添加非空判断。

People people = new People();
People user = people.getUser("name");
if (user != null) {
    String name = user.getName();
    System.out.println(name);
}


2. 定义对象时的初始化

在自己定义对象的时候,注意初始化的值可不可以为null

// String str = "";             // 初始化为空字符串
People people = newPeople();    // 初始化为对象
People user = people.getUser("name");


3. 使用 equals() 方法注意

已知非空对象为调用方,比如将常量值、枚举值作为调用方,避免使用未知对象去调用方法,可有效避免NPE。

String str = "123", string = null;
System.out.println(str.equals(string));

4. 使用 valueOf() 代替 toString() 方法

使用toString()方法要利用对象去调用方法,而对象在不清楚是否为null的情况下,会抛出NPE。使用valueOf()方法可以避免使用未知对象去调用方法来避免。

People people = null;
System.out.println(String.valueOf(people));   // print: null
System.out.println(people.toString());        // NPE


5. 使用开源库非空判断方法

推荐使用各大开源库的StringUtils字符串和CollectionUtils集合等工具进行非空判断。

String str = null;
List<String> list = null;
if (!StringUtils.isEmpty(str)) 
     System.out.println(str);
}
if (!CollectionUtils.isEmpty(list)) {
     System.out.println(list);
}


6. 方法返回空集合或空数组

在方法中返回空数组和空集合而不是返回null,JDK自带的Collections集合工具类提供了多种空集合的定义。

public People[] getUsersArr() {
     return new People[]{};
}
public List<People> getUsers() {
     // return Collections.emptyMap();
     // return Collections.emptySet();
     return Collections.emptyList();
}


7. 定义数据库字段是否为空

在一些特定字段根据业务确定是否可为空,以及合理设置默认值。比如:表示业务状态的字段。

CREATE TABLE user{
    ...
    status NOT NULL DEFAULT 0
    ...
}

8. 使用JDK1.8的Optional

在JDK1.8后提供了防止NPE特定的容器,后面讲到。


    目录
    相关文章
    |
    2月前
    |
    C++
    【编码狂想】指针航行,链表魔法,解锁结构体和类的编程幻境
    【编码狂想】指针航行,链表魔法,解锁结构体和类的编程幻境
    63 1
    |
    6天前
    |
    Java
    2022蓝桥杯大赛软件类国赛Java大学B组 左移右移 空间换时间+双指针
    2022蓝桥杯大赛软件类国赛Java大学B组 左移右移 空间换时间+双指针
    15 3
    |
    6天前
    |
    存储 Java C#
    C++语言模板类对原生指针的封装与模拟
    C++|智能指针的智能性和指针性:模板类对原生指针的封装与模拟
    |
    4天前
    |
    C++
    C++职工管理系统(类继承、文件、指针操作、中文乱码解决)
    C++职工管理系统(类继承、文件、指针操作、中文乱码解决)
    7 0
    C++职工管理系统(类继承、文件、指针操作、中文乱码解决)
    |
    11天前
    类与对象\this指针
    类与对象\this指针
    8 0
    |
    25天前
    |
    存储 安全 编译器
    C++进阶之路:探索访问限定符、封装与this指针的奥秘(类与对象_上篇)
    C++进阶之路:探索访问限定符、封装与this指针的奥秘(类与对象_上篇)
    9 0
    |
    2月前
    |
    存储 编译器 C语言
    【C++】类与对象【定义、访问限定符、this指针】
    【C++】类与对象【定义、访问限定符、this指针】
    11 1
    |
    2月前
    |
    存储 编译器 程序员
    从C语言到C++④(第二章_类和对象_上篇)->类->封装->this指针(下)
    从C语言到C++④(第二章_类和对象_上篇)->类->封装->this指针
    14 0
    |
    2月前
    |
    存储 编译器 C语言
    从C语言到C++④(第二章_类和对象_上篇)->类->封装->this指针(中)
    从C语言到C++④(第二章_类和对象_上篇)->类->封装->this指针
    13 0
    |
    2月前
    |
    Java C语言 C++
    从C语言到C++④(第二章_类和对象_上篇)->类->封装->this指针(上)
    从C语言到C++④(第二章_类和对象_上篇)->类->封装->this指针
    18 0