Skip to content

Optional java8新特性

Optional 类是 Java 8 才引入的,Optional 是个容器,它可以保存类型 T 的值,或者仅仅保存 null。Optional 提供了很多方法,这样我们就不用显式进行空值检测。Optional 类的引入很好的解决空指针异常

Java8之前的空指针异常判断

Java 在使用对象过程中,访问任何方法或属性都可能导致 NullPointerException:

例如我们通过以下方法,获取存在 student 对象中的 Age 值。

    public String getIsocode (Student student){
        return student.getAge();
    }

在这样的示例中,如果我们想要避免由 student 或 student.age 为空而导致的空指针问题,我们就需要采用防御式检查减少 NullPointerException(在访问每一个值之前对其进行明确地检查):

   public String getIsocode (Student student){
        if (null == student) {
            // doSomething
            return "Unknown";
        }
        if (null == student.getAge()) {
            // doSomething
            return "Unknown";
        }
        return student.getAge();
    }

Java8之后Optional的使用

当需要判断的量多时,此时的这些判断语句可能会导致代码臃肿冗余,为此 Java8 特意推出了 Optional 类来帮助我们去处理空指针异常。

Optional类常用方法总结

Optional对象创建

Optional.empty()方法

使用 Optional.empty() 方法声明一个空的 Optional:

// 通过静态工厂方法 Optional.empty(),创建一个空的 Optional 对象
Optional<Student> optStudent = Optional.empty();

Optional.of(T t)方法

使用 Optional.of(T t) 方法创建一个包含非空值的 Optional 对象 (不推荐):

// 静态工厂方法 Optional.of(T t),依据一个非空值创建一个 Optional 对象
Optional<Student> optStudent = Optional.of(student);

如果 student 为 null,这段代码会立即抛出一个 NullPointerException,而不是等到访问 student 的属性值时才返回一个错误。

Optional.ofNullable(T t)方法

使用 Optional.ofNullable(T t) 方法创建一个包含可能为空的值的 Optional 对象 (推荐):

// 用静态工厂方法 Optional.ofNullable(T t),你可以创建一个允许 null 值的 Optional 对象

Optional<Student> optStudent = Optional.ofNullable(student);

Optional对象获取

get()方法

get() 方法,如果变量存在,它直接返回封装的变量值,否则就抛出一个 NoSuchElementException 异常,不推荐使用:

optional.map(Student::getAge).get()
orElse(T other)方法

orElse(T other) 方法,它允许你在 Optional 对象不包含值时提供一个默认值:

optional.map(Student::getAge).orElse(20));
orElseGet(Supplier<? extends T> other)方法

orElseGet(Supplier<? extends T> other) 方法,它是 orElse 方法的延迟调用版,Supplier 方法只有在 Optional 对象不含值时才执行调用(懒加载):

optional.map(Student::getAge).orElseGet(() -> Integer.MAX_VALUE);
orElseThrow(Supplier<? extends X> exceptionSupplier)方法

orElseThrow(Supplier<? extends X> exceptionSupplier) 方法,它和 get 方法非常类似,它们遭遇 Optional 对象为空时都会抛出一个异常,但是使用 orElseThrow 可以定制希望抛出的异常类型:

optional.orElseThrow(() -> new RuntimeException("student不存在!"));
ifPresent(Consumer<? super T> consumer)方法

ifPresent(Consumer<? super T> consumer) 方法,它让能在变量值存在时执行一个作为参数传入的方法,否则就不进行任何操作:

optional.ifPresent(o -> o.setAge(18));

Optional对象中值的提取和转换

map()方法

map() 方法,如果值存在,就对该值执行提供的 mapping 函数调用,如果值不存在,则返回一个空的 Optional 对象。

引入 Optional 以前:

	String name = null;
if(insurance != null){
    name = insurance.getName();
}

引入 Optional 以后:

	Optional&lt;String&gt; name = Optional.ofNullable(insurance).map(Insurance::getName);

Optional 的 map 方法和 Java 8 中 Stream 的 map 方法相差无几。

flatMap()方法

flatMap() 方法,对于嵌套式的 Optiona 结构,我们应该使用 flatMap 方法,将两层的 Optional 合并成一个。

我们试着重构以下代码:

public String getCarInsuranceName(Person person) { return person.getCar().getInsurance().getName(); }

由于我们刚刚学习了如何使用 map,我们的第一反应可能是我们可以利用 map 重写之前的代码:

  Optional&lt;Person&gt; optPerson = Optional.of(person);
      Optional&lt;String&gt; name =
          optPerson.map(Person::getCar)
                   .map(Car::getInsurance)
                   .map(Insurance::getName);

不幸的是,这段代码无法通过编译。为什么呢? optPerson 是 Optional<Person> 类型的 变量, 调用 map 方法应该没有问题。但 getCar 返回的是一个 Optional<Car> 类型的对象,这意味着 map 操作的结果是一个 Optional<Optional<Car>> 类型的对象。因此,它对 getInsurance 的调用是非法的。

下面应用 map 和 flatMap 对上述示例进行重写:

  public String getCarInsuranceName(Optional&lt;Person&gt; person) {
      return person.flatMap(Person::getCar)
                       .flatMap(Car::getInsurance)
                       .map(Insurance::getName)
                       .orElse("Unknown"); // 如果Optional的结果 值为空设置默认值
  }

Optional对象其他方法

isPresent()方法

可以使用 isPresent() 方法检查 Optional 对象是否包含非空值,例如:

Optional&lt;String&gt; optional = Optional.of("Hello World");
if (optional.isPresent()) { 
    System.out.println(optional.get()); 
}
filter()方法

filter() 方法接受一个谓词作为参数。如果 Optional 对象的值存在,并且它符合谓词的条件,filter 方法就返回其值,否则它就返回一个空的 Optional 对象。

比如,你可能需要检查保险公司的名称是否为 “Cambridge-Insurance”。

Insurance insurance = ...;
if(insurance != null && "CambridgeInsurance".equals(insurance.getName())){
		System.out.println("ok");

}

使用 Optional 对象的 filter 方法,这段代码可以重构如下:

Optional&lt;Insurance&gt; optInsurance = ...;
optInsurance.filter(insurance -> "CambridgeInsurance".equals(insurance.getName()))
            .ifPresent(x -> System.out.println("ok"));

Arrays

Arrays.toString()方法

快速输出数组内容

java
int[] a = {1,2,3,4,5};
System.out.println(Arrays.toString(a));
// 输出格式:[1,2,3,4,5]

Arrays.sort()方法

给数组排序,默认升序

java
int[] a = new int[5]{54321};
Arrays.sort(a); // 1 2 3 4 5
System.out.println(Arrays.toString(a));
// [1,2,3,4,5]

①.Arrays.sort(数组名)

②.Arrays.sort(数组名,起始下标,排序个数)

java
Scanner s = Scanner(System.in);
int n = s.nextInt();
int[] a = new int[n]
for(int i = 0; i < n; i++)
   a[i] = s.nextInt();
Arrays.sort(a,0,n - 1);

Arrays.equals()方法

比较两个数组内容是否相等

int[] a = {1,2,3};
int[] b = {1,2,3};
boolean isSame = Arrays.equals(a,b);
//true

Arrays.binarySearch()

在数组中查找元素

再数组中查找指定值,若找到,则返回此值的下标,

若没找到,返回 -插入点-1;

java
int Arrays.binarySearch( Datatype[], Datatype key)

Arrays.copyOf()

拷贝数组

第一个参数是原数组,第二个参数是拷贝长度,返回值是将原数组拷贝一份返回

public static void main(String[] args) {
        int[] arr1 = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        int[] arr2 = new int[5];
        arr2 = Arrays.copyOf(arr1, 10);
}

Arrays.copyOf()的拷贝是从下标0开始的,如果你想从其他下表开始,可以使用Arrays.copyOfRange()方法

java
// from 表示开始位置, to 表示结束位置  
// 复制下标为 :[from, to)
Arrays.copyOfRange(int[] original, int from, int to)

Arrays.sort()[降序排列]

Arrays.asList()

源码

public static &lt;T&gt; List&lt;T&gt; asList(T... a) {
    return new ArrayList&lt;T&gt;(a);
}

使用该方法 可以将一个变长参数或者数组转换成List

需要注意以下问题

public class ArraysAsListTest {
	
	public static void main(String[] args) {
		
		int[] a = {1,2,3};
		Integer[] b = {1,2,3};
		
		List listA = Arrays.asList(a);
		List listA1 = Arrays.asList(1,2,3);
		List listB = Arrays.asList(b);
		
		System.out.println(listA.size());//out:1
		System.out.println(listA1.size());//out:3
		System.out.println(listB.size());//out:3
	}
}

用int类型的数组作为参数为什么输出size是1,使用Integer类型size就是3了呢。

再看源码,asList接收的是一个泛型变长参数,而我们知道基本类型是不能泛型化的,就是说8种基本类型不能作为泛型参数,要想作为泛型参数就要使用其所对应的包装类。

但是listA的Size为什么是1呢,这是因为listA传递的是一个int类型的数组,数组是一个对象,它是可以泛型化的,也就是说例子中是把一个int类型的数组作为了T的类型,所以转换后在List中就只有一个类型为int数组的元素。 后边ListA1与ListB也就可以理解了,一个是进行了自动打包,一个是本来就是包装类型

Util工具类的使用

BooleanUtil: 处理布尔值的判定和转换,常用 isTrue/isFalse 做 null 安全判断,toBoolean(Object) 把字符串/数字等转成 boolean。

StrUtil: 处理字符串的判空、截取、替换、格式化等,常用 isBlank/isEmpty/hasBlank 校验字符串,format 拼接、sub/replace 操作文本。

JSONUtil: 处理 JSON 的序列化/反序列化,常用 toJsonStr 把对象转 JSON 字符串,toBean/toList 把 JSON 转成 Java 对象或集合,以及 parseObj 获取 JSONObject 方便访问字段。