Java中避免空指针异常的方法

这篇文章主要介绍了Java中避免空指针异常的方法,本文讨论Optional类型、Objects类等技术,需要的朋友可以参考下

没人会喜欢空指针异常!有什么方法可以避免它们吗?或许吧。。

本文将讨论到以下几种技术

1.Optional类型(Java 8中新引入的)

2.Objects类(Java 7中原有的)

Java 8中的Optional类

它是什么?

1.Java 8中新引入的类型

2.它是作为某个指定类型的对象的包装器或者用于那些不存在对象(null)的场景

简单来说,它是处理空值的一个更好的替代品(警告:乍一看可能并没有那么明显)

基本用法

它是一种类型(一个类)――那么,怎么才能创建一个这个类型的实例?

使用下它的三个静态方法就可以了:

复制代码 代码如下:

public static Optional stringOptional(String input) {

    return Optional.of(input);

}

简单明了――创建一个包含这个值的Optional包装器。记住――如果这个值是null的话,它会抛出NPE!

复制代码 代码如下:

public static Optional stringNullableOptional(String input) {

if (!new Random().nextBoolean()) {

input = null;

}

return Optional.ofNullable(input);

}

我个人认为是要更好一点。这样就不会有NPE的风险了――如果输入为null的话,会返回一个空的Optional。

复制代码 代码如下:

public static Optional emptyOptional() {

return Optional.empty();

}

如果你真的就是希望返回一个”空"值的话。“空”值并不意味着null。

好吧,那如何去消费/使用Optional呢?

复制代码 代码如下:

public static void consumingOptional() {

Optional wrapped = Optional.of("aString");

if (wrapped.isPresent()) {

System.out.println("Got string - " + wrapped.get());

}

else {

System.out.println("Gotcha !");

}

}

简单的方法就是检查Optional包装器是否真的有值(使用isPresent方法)――你会怀疑这和使用if(myObj != null)相比有什么好处。别担心,这个我会解释清楚的。

复制代码 代码如下:

public static void consumingNullableOptional() {

String input = null;

if (new Random().nextBoolean()) {

input = "iCanBeNull";

}

Optional wrapped = Optional.ofNullable(input);

System.out.println(wrapped.orElse("default"));

}

你可以使用orElse方法,这样万一封装的确实是一个null值的话可以用它来返回一个认值――它的好处显而易见。在提取出真实值的时候可以避免调用ifPresent方法这样明显多余的方式了。

复制代码 代码如下:

public static void consumingEmptyOptional() {

String input = null;

if (new Random().nextBoolean()) {

input = "iCanBeNull";

}

Optional wrapped = Optional.ofNullable(input);

System.out.println(wrapped.orElseGet(

() -> {

return "defaultBysupplier";

}

 

));

}

这个我就有点搞不清楚了。为什么有两个同样目的的不同方法?orElse和orElseGet明明可以重载的(同名但不同参数)。

不论如何,这两个方法明显的区别就在于它们的参数――你可以选择使用lambda表达式而不是supplier的实例来完成这个(一个函数式接口)

为什么使用Optional要比常见的null检查强?

1.使用Optional最大的好处就是可以更明白地表述你的意图――返回null值的话会让消费者感到疑惑(当真的出现NPE的时候)这是不是故意返回的,因此还得查看javadoc来进一步定位。而使用Optional就相当明了了。

2.有了Optional你就可以彻底避免NPE了――如上所提,使用Optional.ofNullable,orElse以及orElseGet可以让我们远离NPE。

一个救星!

看下这个代码片段

复制代码 代码如下:

package com.abhirockzz.wordpress.npesaviors;

 

import java.util.Map;

import java.util.Objects;

 

public class UsingObjects {

 

String getVal(Map aMap, String key) {

return aMap.containsKey(key) ? aMap.get(key) : null;

}

 

public static void main(String[] args) {

UsingObjects obj = new UsingObjects();

obj.getVal(null, "dummy");

}

}

哪个可能会为空?

1.Map对象

2.进行搜索使用的key

3.方法调用的这个实例

如果抛出NPE的话,我们怎么能确定到底是哪个是null的?

复制代码 代码如下:

package com.abhirockzz.wordpress.npesaviors;

 

import java.util.Map;

import java.util.Objects;

 

public class UsingObjects {

String getValSafe(Map aMap, String key) {

Map safeMap = Objects.requireNonNull(aMap,

"Map is null");

String safeKey = Objects.requireNonNull(key, "Key is null");

 

return safeMap.containsKey(safeKey) ? safeMap.get(safeKey) : null;

}

 

public static void main(String[] args) {

UsingObjects obj = new UsingObjects();

obj.getValSafe(null, "dummy");

}

}

requireNonNull方法

1.如果对象不为null的话就返回它本身

2.如果值为null的话,返回的NPE会带有指定的消息

为什么比if(myObj!=null)要好?

你所看到的栈跟踪信息会很清楚地看见Objects.requireNonNull的方法调用。这个再配合你自己的错误日志,可以让你更快地定位问题。。。至少在我看来是更快。

你还可以自己自义校验器,比如说实现一个简单的校验器来确保没有空值。

复制代码 代码如下:

import java.util.Collections;

import java.util.List;

import java.util.Objects;

import java.util.function.Predicate;

 

public class RandomGist {

 

    public static T requireNonEmpty(T object, Predicate predicate, String msgToCaller){

        Objects.requireNonNull(object);

        Objects.requireNonNull(predicate);

        if (predicate.test(object)){

            throw new IllegalArgumentException(msgToCaller);

        }

        return object;

    }

 

    public static void main(String[] args) {

       

    //Usage 1: an empty string (intentional)

 

    String s = "";

    System.out.println(requireNonEmpty(Objects.requireNonNull(s), (s1) -> s1.isEmpty() , "My String is Empty!"));

 

    //Usage 2: an empty List (intentional)

    List list =  Collections.emptyList();

    System.out.println(requireNonEmpty(Objects.requireNonNull(list), (l) -> l.isEmpty(), "List is Empty!").size());

 

    //Usage 3: an empty User (intentional)

    User user = new User("");

    System.out.println(requireNonEmpty(Objects.requireNonNull(user), (u) -> u.getName().isEmpty(), "User is Empty!"));

}

 

    private static class User {

        private String name;

 

        public User(String name){

            this.name = name;

        }

 

        public String getName(){

            return name;

        }

    }

}

不要让NPE在错误的地方成为痛苦。我们有许多工具能更好地处理NPE,甚至彻底地根除它们!

相关文章

Java中的String是不可变对象 在面向对象及函数编程语言中,不...
String, StringBuffer 和 StringBuilder 可变性 String不可变...
序列化:把对象转换为字节序列的过程称为对象的序列化. 反序...
先说结论,是对象!可以继续往下看 数组是不是对象 什么是对...
为什么浮点数 float 或 double 运算的时候会有精度丢失的风险...
面试题引入 这里引申出一个经典问题,看下面代码 Integer a ...