Java8的新特性
1、Java8的概述
- Java8是 Java 语言的一个重要版本,该版本于2014年3月发布,是自Java5以来最具革命性的版本,这个版本包含语言、编译器、库、工具和JVM等方面的十多个新特性。
- Java8版本目前来说还是主流,现在仍有很多公司还在使用该版本。而且该版本是JavaJDK版本中比较跨时代的一个重要版本,因为一下增加了十多个新特性,变化非常之大。
- 新特性就是讲Java新的JDK中增加的额外的新的语法知识和API等等。前面的学习中穿插了很多java新特性。比如:接口中增加了默认方法、静态方法,接口中又增加了私有的方法,简化了编译运行可以一次性完成等等。这里额外补充一些新特性。
2、函数式接口
-
函数式接口主要指只包含一个抽象方法的接口(比抽象类还抽象的类称为接口,体现在所有的量都是常量,所有的方法都是抽象方法,后面增加的新特性除外),如:java.lang.Runnable(run)、java.util.Comparator(compare)接口等。这个新特性是由其他语言发展而来,所以叫函数式接口,而不是方法式接口。
-
函数式接口说白了本质上还是接口。新增了一个规则:当一个接口中只包含了一个抽象方法时,我们称其为函数式接口。
-
Java8提供==@FunctionalInterface注解来定义函数式接口==,若定义的接口不符合函数式的规范便会报错。加了该注解是为了说明下面接口是一个函数式接口,如果下面接口不满足函数式接口的规范,就报错。不加这个只要里面是只有一个抽象方法,那么它也是一个函数式接口。
-
Java8中增加了java.util.function包,该包包含了常用的函数式接口(这就意味着以后只要涉及到只有一个抽象方法这样的函数式接口就不用自己写了,直接拿着Java官方提供的模板用就行了,也就意味着以后我们不用再自个写函数式接口了,直接将Java官方的拿来用就行),具体如下:当然,也可以自己写
接口名称 方法声明 功能介绍 Consumer void accept(T t) 根据指定的参数执行操作,只有参数没有返回值,消费型接口 Supplier T get() 得到一个返回值,没有参数但有返回值,供给型接口 Function<T,R> R apply(T t) 根据指定的参数执行操作并返回,既有参数又有返回值,函数型接口 Predicate boolean test(T t) 判断指定的参数是否满足条件,有参数又有返回值,但返回值是一个boolean类型,它是用来判断一个条件是否满足的,断言型接口--------用于做一些判断操作的 package com.atguigu.exer; @FunctionalInterface public interface MyFun { String getValue(String str); }
package com.atguigu.exer; @FunctionalInterface public interface MyFun2<T, R> { R getValue(T t1, T t2); }
package com.atguigu.exer; import java8.day01.Employee; import org.junit.Test; import java.util.Arrays; import java.util.Collections; import java.util.List; public class TestLambda { List<Employee> employees = Arrays.asList( new Employee("张三", 18, 999), new Employee("李四", 38, 555), new Employee("王五", 50, 66), new Employee("赵六", 16, 333), new Employee("田七", 8, 77.77) ); // 1、调用Collections.sort()方法,通过定制排序比较两个Employee(先按年龄比,年龄相同按姓名比),使用Lambda表达式作为参数传递 @Test public void test1() { Collections.sort(employees, (e1, e2) -> { if (e1.getAge() == e2.getAge()) { return e1.getName().compareTo(e2.getName()); } else { return -Integer.compare(e1.getAge(), e2.getAge()); } }); for (Employee emp : employees) { System.out.println(emp); } } /** * 2、1 声明函数式接口,接口中声明抽象方法,public String getValue(String str); * 2 声明类TestLambda,类中编写方法使用接口作为参数,将一个字符串转换成大写,并作为方法的返回值 * 3 再将一个字符串的第二个和第4个索引位置进行截取子串 */ // 需求:用于处理字符串 public String strHandler(String str, MyFun mf) { return mf.getValue(str); } @Test public void test2() { // 去除字符串所有空格 String trimStr = strHandler("\t\t\t 我淳神威武666 ", str -> str.trim()); System.out.println(trimStr); String upper = strHandler("abcdef", str -> str.toUpperCase()); System.out.println(upper); System.out.println(strHandler("abcdef", str -> str.substring(2, 5)).toUpperCase()); } /** * 3、1 声明一个带两个泛型的函数式接口,泛型类型为<T, R> T为参数,R为返回值 * 2 接口中声明对应抽象方法 * 3 在TestLambda类中声明方法,使用接口作为参数,计算两个long型参数的和 * 4 再计算两个long型参数的乘积 */ // 需求:对于两个Long型的数据进行处理 public void op(Long l1, Long l2, MyFun2<Long, Long> mf) { System.out.println(mf.getValue(l1, l2)); } @Test public void test3() { op(100L, 200L, (x, y) -> x + y); op(100L, 200L, (x, y) -> x * y); } }
package java8.day01; public class Employee { private String name; private int age; private double salary; }
package java8.day01; public class FilterEmployeeByAge implements MyPredicate<Employee> { @Override public boolean test(Employee employee) { return employee.getAge() >= 35; } }
package java8.day01; public class FilterEmployeeBySalary implements MyPredicate<Employee> { @Override public boolean test(Employee employee) { return employee.getSalary() > 200; } }
package java8.day01; @FunctionalInterface public interface MyFun { public Integer getValue(Integer num); }
package java8.day01; public interface MyPredicate<T> { public boolean test(T t); }
package java8.day01; import org.junit.Test; import javax.crypto.spec.PSource; import java.lang.reflect.Array; import java.util.*; public class TestLambda { // 原来的匿名内部类 @Test public void test1() { Comparator<Integer> com = new Comparator<Integer>() { public int compare(Integer integer, Integer t1) { return Integer.compare(integer, t1); } }; TreeSet<Integer> ts = new TreeSet<>(com); } // Java8中Lambda表达式可以解决匿名内部类代码冗余的问题 @Test public void test2() { Comparator<Integer> com = (x, y) -> Integer.compare(x, y); TreeSet<Integer> ts = new TreeSet<>(com); } List<Employee> employees = Arrays.asList( new Employee("张三", 18, 999), new Employee("李四", 38, 555), new Employee("王五", 50, 66), new Employee("赵六", 16, 333), new Employee("田七", 8, 77.77) ); // 需求:获取当前公司中员工年龄大于35的员工信息 public List<Employee> filterEmployees(List<Employee> list) { List<Employee> emps = new ArrayList<>(); for (Employee emp : list) { if (emp.getAge() >= 35) { emps.add(emp); } } return emps; } @Test public void test3() { System.out.println(filterEmployees2(employees)); } // 需求:获取当前公司中员工工资大于500的员工信息 public List<Employee> filterEmployees2(List<Employee> list) { ArrayList<Employee> emps = new ArrayList<>(); for (Employee emp : list) { if (emp.getSalary() > 500) { emps.add(emp); } } return emps; } // 优化方式一:策略设计模式 缺点:每给出一种策略就需要重新写个类实现接口 public List<Employee> filterEmployee(List<Employee> list, MyPredicate<Employee> mp) { List<Employee> emps = new ArrayList<>(); for (Employee emp : list) { if (mp.test(emp)) { emps.add(emp); } } return emps; } @Test public void test4() { List<Employee> employees = filterEmployee(this.employees, new FilterEmployeeByAge()); for (Employee emp : employees) { System.out.println(emp); } System.out.println("---------------------------------"); for (Employee emp : filterEmployee(this.employees, new FilterEmployeeBySalary())) { System.out.println(emp); } System.out.println("----------------------------------"); for (Employee emp : filterEmployee(this.employees, e -> e.getSalary() > 200)) { System.out.println(emp); } } // 优化方式二:匿名内部类 @Test public void test04() { List<Employee> list = filterEmployee(this.employees, new MyPredicate<Employee>() { @Override public boolean test(Employee employee) { return employee.getSalary() > 200; } }); for (Employee employee : list) { System.out.println(employee); } System.out.println("------------------------"); List<Employee> list1 = filterEmployee(this.employees, new MyPredicate<Employee>() { @Override public boolean test(Employee employee) { return employee.getAge() > 35; } }); for (Employee emp : list1) { System.out.println(emp); } } // 优化方式三:Lambda表达式 @Test public void test05() { for (Employee emp : filterEmployee(employees, (e) -> e.getSalary() >= 200)) { System.out.println(emp); } System.out.println("------------------------------"); List<Employee> list = filterEmployee(this.employees, e -> e.getAge() > 15); list.forEach(System.out::println); } // 优化方式四:Stream API @Test public void test06() { employees.stream() .filter((e) -> e.getSalary() >= 200) .limit(2) .forEach(System.out::println); System.out.println("-----------------------------"); employees.stream() .map(Employee::getName) .forEach(System.out::println); } }
package java8.day01; import org.junit.Test; import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.function.Consumer; /* 一、Lambda表达式的基础语法:Java8中引入了一个新的操作符"->" 该操作符称为箭头操作符或Lambda操作符 箭头操作符将Lambda表达式拆分成两部分: 左侧:Lambda表达式的参数列表,对应接口中抽象方法的参数列表 右侧:Lambda表达式中所需执行的功能,即Lambda体,对应接口中抽象方法的实现 语法格式一:无参数,无返回值 () -> System.out.println("Hello Lambda!"); 语法格式二:有一个参数并且无返回值 (x) -> System.out.println(x); 语法格式三:若只有一个参数,参数的小括号可以省略不写 x -> System.out.println(x); 语法格式四:有两个以上的参数,有返回值并且Lambda体中有多条语句 (x, y) -> { System.out.println("函数式接口"); return Integer.compare(x, y); }; 语法格式五:若Lambda体中只有一条语句,return和大括号都可以省略不写 (x, y) -> Integer.compare(x, y); 语法格式六:Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出 数据类型 , 即“类型推断”, 类型检查还是有的,只是书写语法变得方便了 (Integer x, Integer y) -> Integer.compare(x, y);------>String[] str = {"aaa", "bbb", "ccc"} 上联:左右遇一括号省 下联:左侧推断类型省 横批:能省则省 二、Lambda表达式需要函数式接口的支持 函数式接口:接口中只有一个抽象方法时,称为函数式接口。可以使用@FunctionalInterface修饰(必须是函数式接口) 可以检查是否是函数式接口 */ public class TestLambda2 { @Test public void test1() { int num = 0; // 匿名内部类使用的局部参数 jdk 1.7前,必须是final Runnable r = new Runnable() { @Override public void run() { System.out.println("Hello Lambda!" + num); } }; r.run(); System.out.println("-------------------------"); Runnable r1 = () -> System.out.println("Hello Lambda!!!"); r1.run(); } @Test public void test2() { Consumer<String> con = x -> System.out.println(x); con.accept("淳神威武!!!"); } @Test public void test3() { Comparator<Integer> com = (x, y) -> { System.out.println("函数式接口"); return Integer.compare(x, y); }; com.compare(2, 1); } @Test public void test4() { Comparator<Integer> com = (x, y) -> Integer.compare(x, y); } @Test public void test5() { Comparator<Integer> com = (Integer x, Integer y) -> Integer.compare(x, y); String[] strs = {"aaa", "bbb", "ccc"}; // String[] str; // str = {"aaa", "bbb"}; show(new HashMap<>()); // 在jdk1.7中这个编译是不成功的,jdk1.8中对类型推断进一步加强,可以根据上下文进行推断 } public void show(Map<String, Integer> map) { } // 需求:对一个数进行运算 @Test public void test6() { System.out.println(operation(100, x -> x * x)); System.out.println(operation(200, x -> x + 200)); } public Integer operation(Integer num, MyFun mf) { return mf.getValue(num); } }
3、Lambda表达式
-
Lambda 表达式是实例化函数式接口的重要方式,使用 Lambda 表达式可以使代码变的更加简洁紧凑。
-
lambda表达式:参数列表、箭头符号->和方法体组成,而方法体中可以是表达式,也可以是语句块。
-
语法格式:(参数列表) -> { 方法体; } - 其中()、参数类型、{} 以及return关键字 可以省略。
-
package com.lagou.module5.task02; import java.util.Comparator; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; /** * 当接口类型的引用作为方法的形参时,实参的传递方式: * 1、自定义类实现该接口,将该实现类的对象作为参数传递 * 2、匿名内部类,是一个难点也是一个重点 * 3、Lambda表达式 * * 函数式接口的测试 */ public class FunctionalInterfaceTest { public static void main(String[] args) { // 1.匿名内部类的语法格式: 父类/接口类型 引用变量名 = new 父类/接口类型(){ 方法的重写 }; // Runnable:无参无返回值的方法 Runnable runnable = new Runnable() { @Override public void run() { System.out.println("我是既没有参数又没有返回值的方法!"); } }; runnable.run(); // 我是既没有参数又没有返回值的方法! // 使用lambda表达式实现函数式接口对象的创建: (参数列表)->{方法体;} // Java8中实例化了函数式接口类型的对象也可以使用Lambda表达式,比之前讲匿名内部类的方式还要简洁紧凑。多个参数之间使用,隔开,->是不可省略的 //Runnable runnable1 = () -> { System.out.println("我是既没有参数又没有返回值的方法!"); }; // 有点像if分支语句,{}中只有一条语句时可以省略{}。此时Lambda表达式中的{}中只有一条语句,我们可以省略{} Runnable runnable1 = () -> System.out.println("我是既没有参数又没有返回值的方法!"); runnable1.run(); System.out.println("----------------------------------------------------------------------"); // 有参无返回值 Consumer consumer = new Consumer() { @Override public void accept(Object o) { System.out.println(o + "有参但没有返回值的方法就是我!"); } }; consumer.accept("友情提示:"); // 友情提示:有参但没有返回值的方法就是我! //Consumer consumer1 = (Object o) -> {System.out.println(o + "有参但没有返回值的方法就是我!");}; //Consumer consumer1 = (o) -> System.out.println(o + "有参但没有返回值的方法就是我!"); // 省略了()、参数类型、{}, 自动类型推断 Consumer consumer1 = o -> System.out.println(o + "有参但没有返回值的方法就是我!"); consumer1.accept("友情提示:"); System.out.println("----------------------------------------------------------------------"); Supplier supplier = new Supplier() { @Override public Object get() { return "无参有返回值!"; } }; System.out.println(supplier.get()); // 无参有返回值 //Supplier supplier1 = () -> {return "无参有返回值!";}; Supplier supplier1 = () -> "无参有返回值!"; System.out.println(supplier1.get()); System.out.println("----------------------------------------------------------------------"); Function function = new Function() { @Override public Object apply(Object o) { return o; } }; System.out.println(function.apply("有参有返回值的方法,且参数类型和返回值类型可以不相等")); // 有参有返回值的方法 // return 和 {} 都可以省略 Function function1 = o -> o; System.out.println(function1.apply("有参有返回值的方法")); System.out.println("----------------------------------------------------------------------"); Comparator comparator = new Comparator() { @Override public int compare(Object o1, Object o2) { return 0; } }; System.out.println(comparator.compare(10, 20)); // 0 Comparator comparator1 = (o1, o2) -> 0; System.out.println(comparator1.compare(10, 20)); System.out.println("----------------------------------------------------------------------"); Predicate predicate = new Predicate() { @Override public boolean test(Object o) { return false; } }; System.out.println(predicate.test("hello")); // false Predicate predicate1 = o -> false; System.out.println(predicate1.test("hello")); } }
package java8.Java核心内置函数式接口; import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; /** * Java8中内置的四大核心函数式接口 * * Consumer<T>:消费型接口 * void accept(T t); * * Supplier<T>:供给型接口 * T get(); * * Function<T, R>:函数型接口 * R apply(T t); * * Predicate<T>:断言型接口--------用于做一些判断操作的 * boolean test(T t); */ public class TestLambda { // Consumer<T>:消费型接口-------->用于对传进去的数据进行一定的操作,但是不返回 @Test public void test() { happy(10000.00, m -> System.out.println("淳神喜欢买书,每次消费" + m + "元")); } public void happy(double money, Consumer<Double> con) { con.accept(money); } // Supplier<T>:供给型接口------>用于给你产生一些对象 @Test public void test2() { List<Integer> numList = getNumList(10, () -> (int) (Math.random() * 100)); for (Integer num : numList) { System.out.println(num); } } // 需求:产生指定个数整数,并放入集合中 public List<Integer> getNumList(int num, Supplier<Integer> sup) { List<Integer> list = new ArrayList<>(); for (int i = 0; i < num; i++) { list.add(sup.get()); } return list; } // Function<T,R>:函数型接口 @Test public void test3() { String strTrim = strHandler("\t\t\t\t淳神最帅 !", s -> s.trim()); System.out.println(strTrim); String upper = strHandler("ssdfaghhagdfdg", s -> s.toUpperCase()); System.out.println(upper); String strLength = strHandler("ssdfaghhagdfdg", s -> String.valueOf(s.length())); System.out.println(strLength); String substr = strHandler("ssdfaghhagdfdg", s -> s.substring(2, 5)); System.out.println(substr); } // 需求:用于处理字符串 public String strHandler(String str, Function<String, String> fun) { return fun.apply(str); } // Predicate<T>:断言型接口-------->用于做一些判断操作 @Test public void test4() { List<String> list = Arrays.asList("Hello", "atguigu", "Lambda", "www", "ok"); List<String> strList = filterStr(list, s -> s.length() > 3); for (String str : strList) { System.out.println(str); } } // 需求:将满足条件的字符串放入集合中 public List<String> filterStr(List<String> list, Predicate<String> pre) { List<String> strList = new ArrayList<>(); for (String str : list) { if (pre.test(str)) { strList.add(str); } } return strList; } }
4、方法引用
-
方法引用主要指通过方法的名字来指向一个方法而不需要为方法引用提供方法体,该方法的调用交给函数式接口执行。
-
方法引用使用一对冒号 :: 将类或对象与方法名进行连接,通常使用方式如下:
- 对象的非静态方法引用 ObjectName :: MethodName
- 类的静态方法引用 ClassName :: StaticMethodName
- 类的非静态方法引用 ClassName :: MethodName
- 构造器的引用 ClassName :: new
- 数组的引用 TypeName[] :: new
-
方法引用是在特定场景下lambda表达式的一种简化表示,可以进一步简化代码的编写使代码更加紧凑简洁,从而减少冗余代码。
-
package java8.方法引用和构造器引用; import java8.day01.Employee; import org.junit.Test; import java.io.PrintStream; import java.util.Comparator; import java.util.function.*; /** * 方法引用:若Lambda体中的内容/功能有方法已经实现了,我们可以使用 "方法引用" * (可以理解为方法引用是 Lambda 表达是的另一种表现形式) * * 主要有三种语法格式: * * 对象::实例方法名 * * 类::静态方法名 * * 类::实例方法名 * *实例方法: 非静态方法 * * 使用方法引用前体:需要注意,实现接口的抽象方法的参数列表、返回值类型 和 方法引用的参数列表和返回值类型必须一致 * * 注意: * 1、Lambda体重调用的参数列表与返回值类型,要与函数式接口中的抽象方法的参数列表和返回值类型保持一致! * 2、若 Lambda参数列表中第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时可以使用 类名::实例方法 * * * 二、构造器引用: * * 格式: * * ClassName::new * * 注意:需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致! * * 三、数组引用: * * 格式:Type[]::new */ public class TestMethodRef { // 数组引用: @Test public void test7() { Function<Integer, String[]> fun = x -> new String[x]; String[] strs = fun.apply(10); System.out.println(strs.length); Function<Integer, String[]> fun2 = String[]::new; String[] str2 = fun2.apply(20); System.out.println(str2.length); } // 对象::实例方法 @Test public void test1() { Consumer consumer = x -> System.out.println(x); Consumer<String> con = System.out::println; PrintStream ps = System.out; Consumer<String> con1 = ps::println; con.accept("abcdef"); } @Test public void test2() { Employee emp = new Employee(); emp.setName("淳神"); emp.setAge(18 ); Supplier<String> sup = () -> emp.getName(); System.out.println(sup.get()); System.out.println("---------------------"); Supplier<Integer> sup1 = emp::getAge; System.out.println(sup1.get()); } // 类::静态方法 @Test public void test3() { Comparator<Integer> com = (x, y) -> Integer.compare(x, y); Comparator<Integer> com1 = Integer::compare; } // 类::实例方法名 第一个参数是方法的调用者,第二个参数是我们要调用方法的参数时,我们就可以使用:类名::实例方法名 @Test public void test4() { BiPredicate<String, String > bp = (x, y) -> x.equals(y); BiPredicate<String, String> bp2 = String::equals; } // 构造器引用 @Test public void test5() { Supplier<Employee> sup = () -> new Employee(); Employee employee = sup.get(); // 构造器引用:构造器的参数列表要和函数式接口抽象方法的参数列表一致 Supplier<Employee> sup1 = Employee::new; Employee employee1 = sup1.get(); System.out.println(employee1); } @Test public void test6() { // 调用哪个构造器取决于Function接口的抽象方法的参数列表 Function<Integer, Employee> fun = Employee::new; Employee emp = fun.apply(18); System.out.println(emp); BiFunction<Integer, Double, Employee> bf = Employee::new; Employee emp1 = bf.apply(18, 200000.73); System.out.println(emp1); } }
5、Stream接口
-
案例题目:
准备一个List集合并放入Person类型的对象,将集合中所有成年人过滤出来放到另外一个集合并打印出来。
-
基本概念
- java.util.stream.Stream接口是对集合功能的增强,可以对集合元素进行复杂的查找、过滤、筛选等操作。
- Stream接口借助于Lambda 表达式极大的提高编程效率和程序可读性,同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势。
-
使用步骤
- 创建Stream,通过一个数据源来获取一个流。
- 转换Stream,每次转换返回一个新的Stream对象。
- 对Stream进行聚合操作并产生结果。
-
创建方式
- 方式一:通过调用集合的默认方法来获取流,如:default Stream stream()
- 方式二:通过数组工具类中的静态方法来获取流,如:static IntStream stream(int[] array)
- 方式三:通过Stream接口的静态方法来获取流,如:static Stream of(T… values)
- 方式四:通过Stream接口的静态方法来获取流,static Stream generate(Supplier<? extends T> s)
-
中间操作
-
筛选与切片的常用方法如下:
方法声明 功能介绍 Stream fifilter(Predicate<? super T> predicate) 返回一个包含匹配元素的流 Stream distinct() 返回不包含重复元素的流 Stream limit(long maxSize) 返回不超过给定元素数量的流 Stream skip(long n) 返回丢弃前n个元素后的流 -
映射的常用方法如下:
方法声明 功能介绍 Stream map(Function<? super T,? extends R> mapper) 返回每个处理过元素组成的流 Stream flflatMap(Function<? super T,? extends Stream<? extends R>> mapper) 返回每个被替换过元素组成的流,并将所有流合成一个流 -
排序的常用方法如下:
方法声明 功能介绍 Stream sorted() 返回经过自然排序后元素组成的流 Stream sorted(Comparator<? super T> comparator) 返回经过比较器排序后元素组成的流
-
-
终止操作
-
匹配与查找的常用方法如下:
方法声明 功能介绍 Optional fifindFirst() 返回该流的第一个元素 boolean allMatch(Predicate<? super T> predicate) 返回所有元素是否匹配 boolean noneMatch(Predicate<? super T> predicate) 返回没有元素是否匹配 Optional max(Comparator<? super T> comparator) 根据比较器返回最大元素 Optional min(Comparator<? super T> comparator) 根据比较器返回最小元素 long count() 返回元素的个数 void forEach(Consumer<? super T> action) 对流中每个元素执行操作 -
规约的常用方法如下:
方法声明 功能介绍 Optional reduce(BinaryOperator accumulator) 返回结合后的元素值 -
收集的常用方法如下:
方法声明 功能介绍 <R,A> R collect(Collector<? super T,A,R> collector) 使用收集器对元素进行处理
-
6、Optional类
-
案例题目
判断字符串是否为空,若不为空则打印字符串的长度,否则打印0。
-
基本概念
- java.util.Optional类可以理解为一个简单的容器,其值可能是null或者不是null,代表一个值存在或不存在。
- 该类的引入很好的解决空指针异常,不用显式进行空值检测。
-
常用的方法
方法声明 功能介绍 static Optional ofNullable(T value) 根据参数指定数值来得到Optional类型的对象 Optional map(Function<? super T,? extends U> mapper) 根据参数指定规则的结果来得到Optional类型的对象 T orElse(T other) 若该值存在就返回,否则返回other的数值。