彻底搞懂JDK新特性

162 阅读15分钟

JDK8新特性

Java基础学习---点击进入学习

640 (10).png

1. 接口相关

  • JDK8开始,接口中可以书写普通方法,在返回值类型之前,访问权限修饰符之后,使用default修饰
public interface A{
    void m1();
    void m2();
    public default void m3(){
        
    }
    
}
  • JDK8开始,接口中可以书写静态方法了
public interface B{
    public static void m1(){
        
    }
    
    public static void main(String [] args){
        
    }
    
}

2.集合类相关

  • ArrayList 初始容量改为了0 ,之前版本初始化长度为10
  • HashMap 数据结构改为了 数组 + 单向链表 + 红黑树 ,之前版本为数组+ 单向链表

3. lambda表达式

lambda表达式

函数式编程 代表语言 Haskell 长期以来 Java语言臃肿的语法结构 一直属于一个缺点

Oracle公司在JDK1.8引入了函数式编程的新特性,有以下两个好处

​ 1.优化现有的Java语法啰嗦,臃肿的写法

​ 2.吸纳更多的开发人员使用Java

函数式编程属于SAM(Single Abstract Method)接口的一个语法糖,

只有一个抽象方法的接口,即可以称之为函数式接口,可以使用注解@Functionalinterface修饰

注意:lambda表达式只能用于接口 并且接口中只能有一个抽象方法

函数式编程主要用于优化之前匿名内部类的写法

lambda表达式格式:(形参列表)->方法体


/**
 * @author WHD
 * @description TODO
 * @date 2023/12/5 9:14
 *  lambda表达式
 *
 *  函数式编程 代表语言 Haskell  长期以来 Java语言臃肿的语法结构 一直属于一个缺点
 *  Oracle公司在JDK1.8引入了函数式编程的新特性,有以下两个好处
 *      1.优化现有的Java语法啰嗦,臃肿的写法
 *      2.吸纳更多的开发人员使用Java
 *
 *  函数式编程属于SAM(Single Abstract Method)接口的一个语法糖,
 *  只有一个抽象方法的接口,即可以称之为函数式接口,可以使用注解@Functionalinterface修饰
 *
 *  注意:lambda表达式只能用于接口 并且接口中只能有一个抽象方法
 *
 *  函数式编程主要用于优化之前匿名内部类的写法
 *
 *  lambda表达式格式:(形参列表)->方法体
 */
public class Note {
    public static void main(String[] args) {
        B b1 = new B() {
            @Override
            public void m1() {
                System.out.println("匿名内部类的写法重写B接口m1方法");
            }
        };

        b1.m1();

        // 有多个参数 有返回值 且方法体有多条语句
        E e1 = (a,b)->{
            int sum;
            sum = a + b;
            return sum;
        };

        System.out.println(e1);




        // 无参 无返回值 且方法体只有一条语句
        B b2 = ()-> System.out.println("lambda表达式的方式重写B接口m1方法");

        b2.m1();

        // 有一个参数 没有返回值 且方法体只有一条语句

        C c1 = (int a)-> System.out.println("lambda表达式重写C接口m1方法" + a);

        c1.m1(100);

        C c2 = (b)-> System.out.println("lambda表达式重写C接口m1方法" + b);
        c2.m1(200);

        // 有多个参数 有返回值 且方法体只有一条语句

        D d1 = (int a,int b)-> a + b;
        System.out.println(d1.m1(100, 200));

        F f1 = new F() {
            @Override
            void m1() {

            }
        };


        // F f2 = ()-> System.out.println("abc");
    }
}

abstract class F{
    abstract  void m1();
}

interface E{
    int m1(int a,int b);
}

interface D{
    int m1(int a,int b);
}

interface C{
    void m1(int a);
}
@FunctionalInterface
interface B{
    void m1();

}

4. 方法引用

方法引用 : Method Reference 是在lambda表达式的基础之上 又一种新的语法格式

针对函数式接口中的抽象方法,不自己编写方法体实现,而是引用一个已经存在的方法的方法体

作为函数式接口中唯一一个抽象方法的方法体

构造方法引用 类名 :: new;

实例方法引用 对象名 :: 方法名;

静态方法引用 类名 :: 方法名;


/**
 * @author WHD
 * @description TODO
 * @date 2023/12/5 10:13
 *  方法引用 : Method Reference  是在lambda表达式的基础之上 又一种新的语法格式
 *  针对函数式接口中的抽象方法,不自己编写方法体实现,而是引用一个已经存在的方法的方法体
 *  作为函数式接口中唯一一个抽象方法的方法体
 *
 *
 *
 *  构造方法引用      类名 :: new;
 *  实例方法引用      对象名 :: 方法名;
 *  静态方法引用      类名 :: 方法名;
 *
 */
public class Note {
    public static void main(String[] args) {
        A a1 = Book :: new;
        a1.m1();

        System.out.println("a1 = " + a1);

        B b1 = Book :: new;
        b1.m1("abc", 100, "hello world");

        System.out.println("b1 = " + b1);

        System.out.println("--------------------------------------------------");

        C c1 = System.out :: println;

        c1.m1("hello world");

        System.out.println("--------------------------------------------------");

        String str1 = "abc hello world";

        D<String,Boolean> d1 = str1 :: startsWith;

        System.out.println(d1.m1("123"));

        System.out.println("--------------------------------------------------");

        D<Integer,Character> d2 = str1 :: charAt;

        System.out.println(d2.m1(1));

        System.out.println("--------------------------------------------------");

        D<String,String> d3 = str1 ::concat;

        System.out.println(d3.m1("世界你好"));

        System.out.println("--------------------------------------------------");


        D<Boolean,String> d4 = String :: valueOf;
        System.out.println(d4.m1(true).length());

        D<Double,Double> d5 = Math :: ceil;
        System.out.println(d5.m1(2.5));

        D<Double,Double> d6 = Math :: abs;
        System.out.println(d6.m1(-123.0));

        D<Double,Double> d7 = Math :: floor;
        System.out.println(d7.m1(2.5));

        D<Double,Long> d8 = Math :: round;
        System.out.println(d8.m1(3.5));


    }
}

interface D<P,R>{
    R m1(P p);
}

interface C{
    void m1(String str);
}

interface B{
    void m1(String a, double b, String c);
}
interface A{
    void m1();
}
class Book{
    String author;
    double price;
    String bookName;

    public Book() {
        System.out.println("Book类无参构造方法");
    }

    public Book(String author, double price, String bookName) {
        System.out.println("Book类有参构造方法");
        this.author = author;
        this.price = price;
        this.bookName = bookName;
    }
}



JDK在java.util.function 这个包中提供了大量的函数式接口

这些接口可以分为四种类型

Consumer 消费型接口 void accept(T t) 只接受参数没有返回值

Function<T,R> 功能型接口 R apply(T t) 有参数 有返回值

Predicate 断言型接口 boolean test(T t) 有参数 返回值固定为boolean类型

Supplier 供给型接口 T get() 没有参数 有返回值


import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * @author WHD
 * @description TODO
 * @date 2023/12/5 10:51
 *  JDK在java.util.function 这个包中提供了大量的函数式接口
 *  这些接口可以分为四种类型
 *      Consumer<T>     消费型接口   void accept(T t)    只接受参数没有返回值
 *      Function<T,R>   功能型接口   R apply(T t)        有参数 有返回值
 *      Predicate<T>    断言型接口   boolean test(T t)   有参数 返回值固定为boolean类型
 *      Supplier<T>     供给型接口   T get()             没有参数 有返回值
 */
public class TestFunctionalInterface {
    public static void main(String[] args) {
        Consumer<Integer> consumer = System.out::println;
        consumer.accept(100);

        // -------------------------------------------------------------------

        Function<String,Integer> function1 = Integer :: parseInt;
        System.out.println(function1.apply("123"));

        Function<Double,String> function2 = String :: valueOf;
        System.out.println(function2.apply(25.5));

        String str1 = "abc hello world";

        Function<String,Boolean> function3 = str1 ::equals;

        System.out.println(function3.apply("abc hello world"));

        // -------------------------------------------------------------------

        Predicate<String> predicate1 = str1 :: startsWith;

        System.out.println(predicate1.test("abc"));

        Predicate<String> predicate2 = str1 :: endsWith;

        System.out.println(predicate2.test("abc"));

        // -------------------------------------------------------------------

        Supplier<Double> supplier1 = Math :: random;

        System.out.println(supplier1.get());


    }
}

5.Stream流式编程

Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API。

Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充,因为Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

Stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,负责存储数据,Stream流讲的是计算,负责处理数据!”

注意:

①Stream 自己不会存储元素。

②Stream 不会改变源对象。每次处理都会返回一个持有结果的新Stream。

③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

5.1 创建方式

创建 Stream方式一:通过集合

Java8 中的 Collection 接口被扩展,提供了两个获取流的方法: default Stream stream() : 返回一个顺序流

创建 Stream方式二:通过数组

Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:

static Stream stream(T[] array): 返回一个流

创建 Stream方式三:通过Stream的of()

可以调用Stream类静态方法 of(), 通过显示值创建一个流。它可以接收任意数量的参数。

public static Stream of(T... values) : 返回一个流

创建 Stream方式四:创建无限流(了解)

可以使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流。

public static Stream generate(Supplier s)

5.2 中间操作

方 法描 述
filter(Predicate p)保存符合指定条件的元素
distinct()筛选,通过流所生成元素的equals() 去除重复元素
limit(long maxSize)保留指定个数的前
skip(long n)跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
sorted()产生一个新流,其中按自然顺序排序
map(Function f)接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap(Function f)接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流

5.3 终止操作

方法描述
boolean allMatch(Predicate p)检查是否匹配所有元素
boolean anyMatch(Predicate p)检查是否至少匹配一个元素
boolean noneMatch(Predicate p)检查是否没有匹配所有元素
Optional findFirst()返回第一个元素
long count()返回流中元素总数
Optional max()返回流中最大值
Optional min()返回流中最小值
void forEach(Consumer c)迭代

import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * @author WHD
 * @description TODO
 * @date 2023/12/5 14:06
 */
public class TestStream {
    static List<Student> list = new ArrayList<>();



    // 1.查找地址为深圳的人员信息
    // 2.查找地址为深圳 并且 性别为男  的人员信息
    // 3.查找地址为深圳 并且 性别为男  并且 成绩小于80  的人员信息

    @Test
    public void createStream(){
        // 创建Stream方式1  通过Collection接口中的普通方法 stream()
        Stream<Student> stream = list.stream();

        // 创建 Stream方式二:通过数组
        IntStream stream1 = Arrays.stream(new int[]{1,2,3,4,5});


        // 创建 Stream方式三:通过Stream的of()
        Stream<Character> a = Stream.of('a', 'b', 'c', 'd');


        // 创建 Stream方式四:创建无限流(了解)

//        Stream<Double> generate = Stream.generate(Math::random);
//
//        generate.forEach(System.out::println);


        Stream.generate(Math :: random).limit(5).forEach(System.out::println);



    }

    @Test
    public void filter1(){
        // 1.查找地址为深圳的人员信息
        list.stream().filter((stu)->stu.getAddress().equals("深圳")).forEach(System.out::println);


    }

    @Test
    public void filter2(){
        // 2.查找地址为深圳 并且 性别为男  的人员信息
        list.stream().filter((stu)->stu.getAddress().equals("深圳")).filter((stu)->stu.getSex() == '男').forEach(System.out::println);
    }

    @Test
    public void filter3(){
        // 3.查找地址为深圳 并且 性别为男  并且 成绩小于80  的人员信息
        list.stream().filter((stu)->stu.getAddress().equals("深圳")).filter((stu)->stu.getSex() == '男').filter((stu)->stu.getScore() < 80);
    }


    @Test
    public void distinct(){
        // 去除集合中的重复元素 重复的元素只展示一个
        list.stream().distinct().forEach(System.out::println);
    }


    @Test
    public void limit(){
        // 保留集合中的前5个元素
        list.stream().limit(5).forEach(System.out::println);

        System.out.println("--------------------------------------------");

        // 查找地址为 广州的人员信息 保留前1个

        list.stream().filter((stu)->stu.getAddress().equals("广州")).limit(1).forEach(System.out::println);
    }


    @Test
    public void skip(){
        // 跳过指定个数的元素

        list.stream().skip(5).forEach(System.out::println);
    }


    @Test
    public void sorted(){
        // 针对集合中的元素 按照年龄排序
        list.stream().sorted().forEach(System.out::println);
    }

    @Test
    public void sorted1(){
        list.stream().sorted((s1,s2)->(int)(s1.getScore() - s2.getScore())).forEach(System.out::println);
    }


    @Test
    public void map(){
        // 将一个流中的元素经过加工处理 映射为新的元素
        Stream.of("a","b","c").map((str)-> str.toUpperCase()).forEach(System.out::println);
    }

    @Test
    public void flatMap(){
        // 每个字符串对象调用buildStringToCharacterStream()方法 会得到一个流
        // 调用flatMap方法 将多个流整合为一个流
        // 具体应用场景:
        // 查询年龄大于20的人
        // 查询分数大于80的人
        // 查询性别为男的人
        Stream.of("hello","world","abc").flatMap(TestStream::buildStringToCharacterStream).limit(3).forEach(System.out::println);

    }

    public static Stream<Character> buildStringToCharacterStream(String str){
        List<Character> list = new ArrayList<>();
        for(Character ch : str.toCharArray()){
            list.add(ch);
        }
        return list.stream();
    }
    static{
        Student stu1 = new Student("赵四", 15, '女', "深圳", 80);
        Student stu2 = new Student("富贵", 25, '男', "广州", 90);
        Student stu3 = new Student("小宝", 15, '男', "深圳", 50);
        Student stu4 = new Student("刘能", 35, '男', "广州", 80);
        Student stu5 = new Student("大拿", 25, '男', "东莞", 8);
        Student stu6 = new Student("广坤", 55, '男', "东莞", 85);
        Student stu7 = new Student("玉田", 45, '女', "东莞", 66);
        Student stu8 = new Student("刘英", 55, '女', "云南", 55);
        Student stu9 = new Student("刘英", 45, '女', "惠州", 55);
        Student stu10 = new Student("刘英", 65, '女', "惠州", 55);

        list.add(stu1);
        list.add(stu2);
        list.add(stu3);
        list.add(stu4);
        list.add(stu5);
        list.add(stu6);
        list.add(stu7);
        list.add(stu8);
        list.add(stu9);
        list.add(stu10);
    }

    @Test
    public void allMatch(){
        // boolean allMatch(Predicate p) 判断是否所有元素都符合某个条件
        // 判断是否所有人员都是成年人
        boolean b1 = list.stream().allMatch(stu -> stu.getAge() >= 18);

        System.out.println("b1 = " + b1);

        // 判断地址为深圳 并且 性别为男的人员信息 是否分数都大于50

        boolean b2 = list.stream().
                filter(student -> student.getAddress().equals("深圳")).
                filter(student -> student.getSex() == '男').allMatch(student -> student.getScore() > 40);

        System.out.println("b2 = " + b2);

    }


    @Test
    public void anyMatch(){
        // 判断流中的元素是否至少有一个匹配  anyMatch(Predicate p)
        // 判断集合中是否包含至少一个女性
        boolean b = list.stream().anyMatch(student -> student.getSex() == '女');
        System.out.println("b = " + b);
        // 判断集合中是否包含至少一个分数大于95的人员
        boolean b1 = list.stream().anyMatch(student -> student.getScore() > 95);
        System.out.println("b1 = " + b1);
    }

    @Test
    public void noneMatch(){
        // 判断流中的元素是否都不匹配 noneMatch(Predicate p)
        // 判断集合中是否不包含地址为云南的人员信息
        boolean b1 = list.stream().noneMatch(student -> student.getAddress().equals("云南"));
        System.out.println("b1 = " + b1);
    }

    @Test
    public void findFirst(){
        // 返回流中的第一个元素
        Optional<Student> first = list.stream().findFirst();
        System.out.println("first = " + first);

        // 查找第一个  年龄大于30 并且 地址信息不为空 并且性别为女

        Optional<Student> first1 = list.stream().filter(student -> student.getAge() > 30).filter(student -> student.getAddress() != null).filter(student -> student.getSex() == '女').findFirst();
        System.out.println("first1 = " + first1);

    }

    @Test
    public void count(){
        // count() 统计流中元素的个数

        long count = list.stream().count();
        System.out.println("count = " + count);

        // 统计 地址信息为东莞   并且 分数 大于60的人员个数
        long count1 = list.stream().filter(student -> student.getAddress().equals("东莞")).filter(student -> student.getScore() > 60).count();

        System.out.println("count1 = " + count1);
    }

    @Test
    public void max(){
        // max() 返回流中最大的元素
        Optional<Student> max1 = list.stream().max((s1, s2) -> s1.getAge() - s2.getAge());

        System.out.println("max1 = " + max1);

        // min() 返回流中最小的元素

        Optional<Student> min = list.stream().min((s1, s2) -> s1.getAge() - s2.getAge());
        System.out.println("min = " + min);

    }







}




import java.util.Objects;

/**
 * @author WHD
 * @description TODO
 * @date 2023/12/5 14:06
 */
public class Student implements Comparable<Student> {
    private String name;
    private int age;
    private char sex;
    private String address;
    private double score;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }

    public Student() {
    }

    public Student(String name, int age, char sex, String address, double score) {
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.address = address;
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex=" + sex +
                ", address='" + address + '\'' +
                ", score=" + score +
                '}';
    }



    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        if (sex != student.sex) return false;
        if (Double.compare(student.score, score) != 0) return false;
        if (!Objects.equals(name, student.name)) return false;
        return Objects.equals(address, student.address);
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        result = 31 * result + (int) sex;
        result = 31 * result + (address != null ? address.hashCode() : 0);
        temp = Double.doubleToLongBits(score);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        return result;
    }

    @Override
    public int compareTo(Student stu) {
        return this.getAge() - stu.getAge();
    }
}

6.Optional类

到目前为止,空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google Guava的启发,Optional类已经成为Java 8类库的一部分。

Optional实际上是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。

方法描述
of(Object obj)根据传入对象获取一个Optional对象,此对象不能为null
empty()包装一个保存有null的Optional对象
ofNullable(Object obj)根据传入对象获取一个Optional对象,此对象可以为null
get()获取Optional中保存的对象,如果为null,则报空指针异常
isPresent()表示判断Optional是否为null,为null结果为false,不为null结果为true
ifPresent(Consumer<? super T> consumer)如果Optional对象中的对象不为null,则消费此对象,否则不消费
orElse(T other)如果当前Optional对象中保存对象为null,则使用传入对象
orElseGet(Supplier<? extends T> other)如果当前Optional对象中为null则获取到另外一个对象,否则不获取
orElseThrow(Supplier<? extends X> exceptionSupplier)如果当前Optional对象中为null则抛出异常,否则不抛出

import org.junit.Test;

import java.util.Optional;
import java.util.OptionalInt;

/**
 * @author WHD
 * @description TODO
 * @date 2023/12/6 9:23
 *  Optional类可以保存一个为空 或者 不为空的对象
 *  通过一些方法体现值是否为空 代码阅读性更强 避免了繁琐的 非空判断的过程 使代码更加简洁
 */
public class TestOptional {
    @Test
    public void of(){
        // of(T t) 根据传入对象获取一个Optional对象,此对象不能为null
        String str1 = "abc";
        String str2 = null;
        Optional<String> op1 = Optional.of(str1);
        System.out.println("op1 = " + op1);


        Optional<String> op2 = Optional.of(str2);
        System.out.println("op2 = " + op2);
    }


    @Test
    public void empty(){
        // empty() 包装一个保存有null的Optional对象

        Optional<Object> op1 = Optional.empty();
        System.out.println("op1 = " + op1);
    }

    @Test
    public void ofNullable(){
        // 根据传入对象获取一个Optional对象,此对象可以为null
        String str1 = "abc";
        String str2 = null;
        Optional<String> op1 = Optional.ofNullable(str1);
        System.out.println("op1 = " + op1);

        Optional<String> op2 = Optional.ofNullable(str2);
        System.out.println("op2 = " + op2);
    }

    @Test
    public void get(){
        // get() 获取Optional对象中的对象 如果为空 则抛出空指针异常  NPE异常 NullPointerException

        Optional<String> op1 = Optional.of("abc");
        System.out.println(op1.get());

        Optional<Character> op3 = Optional.ofNullable('A');
        System.out.println(op3.get());

        Optional<Object> op2 = Optional.empty();
        System.out.println(op2.get());
    }

    @Test
    public void isPresent(){
        // isPresent()  表示判断Optional是否为null,为null结果为false,不为null结果为true
        Optional<String> op1 = Optional.of("abc");
        System.out.println(op1.isPresent());

        Optional<Object> op2 = Optional.empty();
        System.out.println(op2.isPresent());

        Optional<Integer> op3 = Optional.ofNullable(123);
        System.out.println(op3.isPresent());
    }

    @Test
    public void ifPresent(){
        // ifPresent(Consumer<? super T> consumer) 如果Optional保存的对象不为空 则消费 为空 则不消费
        Optional<Object> op1 = Optional.of(3.5);
        op1.ifPresent(System.out::println);

        Optional<Object> op2 = Optional.empty();
        op2.ifPresent(System.out::println);

        Optional<Character> op3 = Optional.ofNullable('a');
        op3.ifPresent(System.out::println);
    }

    @Test
    public void orElse(){
        // orElse(T other) 如果Optional对象中保存的对象为空 则使用传入对象 否则不使用
        Optional<Character> op1 = Optional.of('A');

        Character ch1 = op1.orElse('B');

        System.out.println("character = " + ch1);


        Optional<Object> op2 = Optional.empty();

        Object abc = op2.orElse("abc");

        System.out.println("abc = " + abc);

        Optional<Object> op3 = Optional.ofNullable(null);

        Object str = op3.orElse("hello world");

        System.out.println("str = " + str);


    }

    @Test
    public void orElseGet(){
        // orElseGet(Supplier<? extends T> other)
        // 如果当前Optional对象中为null则获取到另外一个对象,否则不获取


        Optional<String> op1 = Optional.of("abc");

        String str1 = "hello world";

        System.out.println(op1.orElseGet(() -> str1.toString()));

        System.out.println("---------------------------------------");

        Optional<Object> op2 = Optional.empty();

        System.out.println(op2.orElseGet(() -> str1.toUpperCase()));

        System.out.println("---------------------------------------");

        Optional<Object> op3 = Optional.ofNullable(null);

        System.out.println(op3.orElseGet(Math::random));
    }

    @Test
    public void orElseThrow(){
        // orElseThrow(Supplier<? extends X> exceptionSupplier)
        // 如果Optional保存的为空 则抛出异常
        // 如果不为空 则抛异常

        Optional<String> op1 = Optional.of("abc");

        op1.orElseThrow(()->new RuntimeException("对象不能为空1"));

        Optional<Object> op3 = Optional.ofNullable(null);
        op3.orElseThrow(()->new RuntimeException("对象不能为空2"));



        Optional<Object> op2 = Optional.empty();
        op2.orElseThrow(()->new RuntimeException("对象不能为空3"));

    }
}