zoukankan      html  css  js  c++  java
  • 空指针异常与Optional类

    摘自:https://www.cnblogs.com/kyoner/p/12101005.html

    空指针异常与Optional类

     

    一、什么是空指针异常

    当程序需要对象实例的时候返回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特定的容器,后面会讲到。

    三、Optional

    Optional是一个可以包含null或者非null的容器对象。根据源码分析方法功能:

    1. 定义方法

    1.1 empty

    返回一个空的Optaional实例,在这个实例中没有值存在。

    public static<T> Optional<T> empty() {    
        @SuppressWarnings("unchecked")    
        Optional<T> t = (Optional<T>) EMPTY;    
        return t;
    }

    1.2 of

    返回一个值不能为空的Optional实例,在这个实例中值为null时抛出NPE。

    public static <T> Optional<T> of(T value) {    
        return new Optional<>(value);
    } 
    private Optional(T value) {    
        this.value = Objects.requireNonNull(value);
    }

    1.3 ofNullable

    返回一个值可以为空的Optional实例,在这个实例中值为null时返回一个空的Optaional实例。

    public static <T> Optional<T> ofNullable(T value) {    
        return value == null ? empty() : of(value);
    }

    2. 功能方法

    2.1 isPresent

    如果有值存在,返回true,否则返回false

    People people = new People();
    System.out.println(Optional.ofNullable(people).isPresent());// print: true 
    people = null;
    System.out.println(Optional.ofNullable(people).isPresent());// print: false

    2.2 get()

    如果有值存在,返回值,否则抛出NoSuchElementException

    People people = new People();
    System.out.println(Optional.ofNullable(people).get());// print: People@61bbe9ba
    people = null;
    System.out.println(Optional.ofNullable(people).get());// print: Exception in thread "main" java.util.NoSuchElementException: No value present

    尽量不要使用该方法获取对象。

    2.3 orElse

    如果有值存在,返回值,否则返回该 orElse 方法的参数,可以用来定义默认值。

    String str = null;
    String result = Optional.ofNullable(str).orElse("default");//print:default
    System.out.println(result);

    2.4 orElseGet

    如果有值存在,返回值,否则返回提供者函数,可以用函数返回值来定义默认值。

    String str = null;
    String result = Optional.ofNullable(str).orElseGet(() -> "ajn");//print:ajn
    System.out.println(result);

    2.5 orElseThrow

    如果有值存在,返回值,否则返回函数接口参数提供的异常。

    String str = null;
    String result = Optional.ofNullable(str).orElseThrow(IllegalArgumentException::new);// print: Exception in thread "main" java.lang.IllegalArgumentException
    System.out.println(result);

    关于更多函数接口的内容,关注Java函数式编程

    2.6 ifPresent

    如果有值存在,方法参数提供的函数接口会进行处理,否则不做任何操作。

    Optional.ofNullable(getPeople()).ifPresent(people -> {    
        System.out.println("the people is nut null: " + people);
    });

    上面代码等价于:

    People people = getPeople();
    if (people != null) {    
        System.out.println("the people is nut null: " + people);
    }

    2.7 filter

    如果有值存在,并且值符合给定的函数条件,返回当前Optional,否则返回一个空的Optaional实例,可以用来过滤特殊值。

    String name = "AiJiangnan";// 给定的条件是字符串包含Ai
    String result = Optional.of(name).filter(str -> str.contains("Ai")).orElse("字符串不包含Ai");
    System.out.println(result);

    2.8 map

    如果有值存在,可以将该值的类型转换成其他类型,并返回转换后类型的Optional实例,否则返回一个空的Optaional实例,可以链式判空,非常实用。

    People people = null;
    String result = Optional.ofNullable(people)    
        .map(People::getName)    
        .map(String::toUpperCase)    
        .orElse("default");
    System.out.println(result);

    只有当people对象不为null,并且people.getName()不为null,才返回name全部转换为大写的字符串,否则都返回 default。

    2.9 flatMap

    如果有值存在,可以将该值的类型转换成其他类型,但最终只能转成 Optional 实例,否则返回一个空的Optaional实例。该方法与map方法类似,只是该方法返回的Optional实例由函数参数返回。

    People people = new People();
    people.setName(" ajn ");
    String result = Optional.ofNullable(people)    
        .flatMap(peo -> Optional.ofNullable(peo.getName()))    
        .flatMap(str -> Optional.of(str.toUpperCase()))    
        .orElse("default");
    System.out.println(result);
     
    标签: java
  • 相关阅读:
    A1023 Have Fun with Numbers (20分)(大整数四则运算)
    A1096 Consecutive Factors (20分)(质数分解)
    A1078 Hashing (25分)(哈希表、平方探测法)
    A1015 Reversible Primes (20分)(素数判断,进制转换)
    A1081 Rational Sum (20分)
    A1088 Rational Arithmetic (20分)
    A1049 Counting Ones (30分)
    A1008 Elevator (20分)
    A1059 Prime Factors (25分)
    A1155 Heap Paths (30分)
  • 原文地址:https://www.cnblogs.com/xichji/p/12105433.html
Copyright © 2011-2022 走看看