1. java 新特性介绍
1.1. 主要新特性
- Lambda 表达式: Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
- 方法引用 : 方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
- 默认方法 :默认方法就是一个在接口里面有了一个实现的方法。
- 1. Nashorn, JavaScr新工具 :新的编译工具,如:Nashorn引擎 jjs、 类依赖分析器jdeps。
- Stream API −新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。
- Date Time API :加强对日期与时间的处理。
- Optional 类 : Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。
- ipt 引擎 : Java 8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用
2. 接口的默认、静态方法
2.1. java8以前的接口
在java8之前Java 中接口里面的方法默认都是 public abstract 修饰的抽象方法并且没有方法体
2.2. 接口中的 static 方法
- 使用 static 修饰接口中的方法, 必须有主体。
- 接口中的 static方法 只能被 接口本身调用:接口名.方法名(...)。
public interface InterfaceText {
void walk();
static void testStatic(){
System.out.println("接口中的静态");
}
}
class IterfaceTestClass{
public static void main(String[] args) {
InterfaceText.testStatic();
}
}
- 接口中的 static方法,不能被 子接口继承。
- 接口中的 static 方法,不能被 实现类 重写 及调用。
2.3. 接口中的 default 方法
在接口中可定义一个使用default修饰有方法体的方法,接口中可以对这个方法提供默认的一种实现。
- 使用 default 修饰接口中的 方法,必须有主体。
- 接口的 default 方法不能够 被接口本身调用,需要接口的实现类(实现对象)来调用。
- 接口的 default 方法可以被 子接口继承、重写、直接调用。
- 接口的 default 方法可以被 实现类重写 及 直接调用。(可以不需要重写即可直接使用)
public interface InterfaceText {
default void testDefault(){
System.out.println("我是接口中的静态方法");
}
}
class defaultTestClass implements InterfaceText{
public void test1(){
new defaultTestClass().testDefault(); // 调用接口中的 default 方法
}
}
3. 函数式接口
3.1. 什么是 函数式接口
- 函数式接口(Functional Interface) 就是一个 有且仅有一个抽象方法。
- 但是函数式接口 可以有多个 非抽象方法(static、default、private)。
- 可以使用 注解 @FunctionalInterface 标记该接口为函数式接口
3.2. 函数式接口注解
@FunctionalInterface
标记 函数式接口,可以用来检查 该接口是否符合函数式接口 格式。
3.3. 常用的函数式接口
3.3.1. Supplier 接口
Supplier: 包含一个无参的方法,有返回值。
T get(): 获得结果。
- 该方法不需要参数,它会按照某种实现逻辑(Lambda表达式实现)返回一个数据。
- Supplier接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用。
public class getString {
public static void main(String[] args) {
/*String s = getString(() -> {
return "小曹";
});*/
String s1 = getString(() -> "小王");
System.out.println(s1);
Integer integer = getInteger(() -> 20);
System.out.println(integer);
}
//定义一个方法,返回一个整数数据
private static Integer getInteger(Supplier<Integer> sup){
return sup.get();
}
//定义一个方法,返回一个字符串数据
private static String getString(Supplier<String> sup){
return sup.get();
}
}
3.3.2. Consumer接口
Consumer表示消耗,接口接受通用参数t,调用accept,对参数执行一系列操作,但没有返回值。
- void accept(T t): 对给定的参数执行操作。
- default Consumer andThen(Consumer after): 返回一个组合的Consumer,一次执行此操作,然后执行after操作。
Consumer接口也被称为消费型接口,它消费的数据的数据类型由泛型指定
public class ConsumerDemo {
public static void main(String[] args) {
/* operatorString("小曹",(String s)->{
System.out.println(s);
});*/
operatorString("小曹",s-> System.out.println(s));
operatorString("小曹",s-> System.out.println(new
StringBuilder(s).reverse().toString()));
//方法引用
//operatorString("小曹",System.out::printf);
operatorString("林青霞",s-> System.out.println(s),s-> System.out.println(new
StringBuilder(s).reverse().toString()));
}
//定义一个方法,用不同的方式消费一个字符串数据两次
private static void operatorString(String name,Consumer<String>
con1,Consumer<String> con2){
/* con1.accept(name);
con2.accept(name);*/
con1.andThen(con2).accept(name);
}
//定义一个方法,消费一个字符串数据
private static void operatorString(String name, Consumer<String> con){
con.accept(name);
}
}
3.3.3. Predicate 接口
Predicate是Java中的一个函数式接口,它代表一个判断逻辑,接收一个输入参数,返回一个布尔值。
Predicate 常用的四个方法
- boolean test(T t):对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值。
- default Predicate negate():返回一个逻辑的否定,对应逻辑非。
- default Predicate and(Predicate other) :返回一个组合判断,对应短路与。
- default Predicate or(Predicate other) :返回一个组合判断,对应短路或。
public class PredicateDemo1 {
public static void main(String[] args) {
/* boolean b = checkString("hello", (String s) -> {
return s.length() > 8;
});
*/ boolean b = checkString("helloword", s -> s.length() > 8);
System.out.println(b);
}
//判断给定的字符串是否满足要求
private static boolean checkString(String s, Predicate<String> pre){
//return pre.test(s);
//return !pre.test(s);
//逻辑非
return pre.negate().test(s);
}
}
3.3.4. Function接口
接口是 Java 8 引入的一个函数式接口,它定义了一个具有单一输入参数和单一结果返回的函数。
Function<T,R> 接口通常用于对参数进行处理,转换(处理逻辑由lambda表达式实现)然后返回一个新的值。
该接口包含一个抽象方法 apply(T t),用于接受一个参数 T 并返回一个结果。
常用方法
- R apply(T t) :用于接受一个参数 T 并返回一个结果。
T 函数输入的类型
R 函数的结果类型
- default Function and Then(Function after): 返回一个组合函数,首先将该函数应用于输入,然后将after函数应用于结果
public class FunctionDemo {
public static void main(String[] args) {
/* convert("100",(String s)->{
return Integer.parseInt(s);
});*/
convert("100",s->Integer.parseInt(s));
//方法引用
convert("100",Integer::new);
convert(100,i->String.valueOf(i+566));
convert("100",s->Integer.parseInt(s),i->String.valueOf(i+566));
}
//定义一个方法,把一个字符串转换为int类型,在控制台输出
private static void convert(String s, Function<String,Integer> fun){
int i = fun.apply(s);
System.out.println(s);
}
//定义一个方法,把一个int类型的数据加上一个整数之后,转为字符串在控制台输出
private static void convert(int i,Function<Integer,String> fun){
String s = fun.apply(i);
System.out.println(s);
}
//定义一个方法,把字符串转换int类型,把int类型的数据加上一个整数之后,转为字符串在控制台
输出
private static void convert(String s,Function<String,Integer>
fun1,Function<Integer,String> fun2){
/* Integer i = fun1.apply(s);
String k = fun2.apply(i);
System.out.println(k);*/
String s1 = fun1.andThen(fun2).apply(s);
System.out.println(s1);
}
}
4. Lambda 表达式(掌握)
4.1. 什么是 lambda 表达式
简单来说:可以看成 匿名内部类的简写,使用Lambda 表达式时,接口必须是函数式接口。
4.2. Lambda 表达式的语法
<函数式接口> <变量名> = (参数1,参数2...) -> {
//方法体
}
4.3. Lambda 的使用
@FunctionalInterface
public interface ISay {
void say();
}
ISay iSay = new ISay() {
@Override
public void say() {
System.out.println("人在说话...");
}
};
iSay.say();x
lambda 表达式 的作用 就是为了简化:匿名内部类。 必须是函数式接口。
ISay iSay1=()->{System.out.println("猫在喵喵喵");};
iSay1.say();
根据左边函数式接口 ,右边能够 自动推导 出写的哪一个方法。
4.4. lambda 表达式特点
参数1,参数2…)表示参数列表;->表示连接符;{}内部是方法体 。
- “=” 右边的类型 会根据 左边的函数式接口类型自动推断。
- 如果形参为空,只需要保留“()”。
- 如果形参只有 1 个,() 可以省略,只需要参数的名称即可。
- 如果执行语句只有1句,且无返回值,{}可以省略;若有返回值,则若想省去{},则必须同时省略return,且执行语句也保证只有1句
- 形参列表的数据类型 会自动推断。
- lambda 不会生成一个单独的 内部类文件。
- lambda 表达式若访问了局部变量,则局部变量必须是final的,
若局部变量没有加 final 关键字,系统会自动添加,此后再修改该局部变量,会报错。
案例1如下:有参函数式接口
public interface Ieat {d
void eat(String food);
}d
public class Human {
public static void main(String[] args) {
//------------------------匿名内部类---------------------
Ieat ieat = new Ieat() {
@Override
public void eat(String food) {
System.out.println("人吃"+food);
}
};
ieat.eat("螺蛳粉");
//----------------------lambda 表达式------------------
Ieat ieat1 = (String food)->{ System.out.println("狗吃"+food);};
ieat1.eat("屎");
// lambda 变形二: 只有一个参数的时候,() 、形参类型也可以省略;
// 只需要参数的名称即可
// 如果执行语句只有1句,且无返回值,{}可以省略
Ieat ieat2 = food -> System.out.println("猫"+food);
}
}
案例二如下:有参有返回值 函数式接口
public interface Iexam {
String exam(Integer num);
}
public class Human {
public static void main(String[] args) {
//------------------------匿名内部类---------------------
Iexam iexam = new Iexam() {
@Override
public String exam(Integer num) {
return num >60 ?"合格":"不合格";
}
};
System.out.println(iexam.exam(60));
//----------------------lambda 表达式------------------
//若有返回值,则若想省去{},则必须同时省略return,且执行语句也保证只有1句
Iexam iexam1 = num -> num>60?"合格啦":"不合格 请继续努力";
System.out.println(iexam1.exam(70));
}
}
4.5. Lambda 表达式 和 匿名内部类的区别?
- 所需类型不同
- 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类。
- Lambda表达式:只能是接口。
- 使用限制不同
- 如果接口中有且仅有一个抽象方法:可以使用Lambda 表达式,也可以使用匿名内部类。
- 如果接口中有多于一个抽象方法,只能使用匿名内部类。
- 实现原理不同
- 匿名内部类:编译之后产生一个单独的.class 字节码文件。
- Lambda 表达式:编译之后,没有单独的. class 字节码文件。对应的字节码会在运行的时候动态生成。
5. lambda中方法引用【掌握】
5.1. 静态方法引用【掌握】
语法 :类名::静态方法名
注意事项:
- 被引用的 静态方法参数列表 和 函数式接口中抽象方法的参数一致。
- 接口的 抽象方法有返回值,引用的静态方法必须有相同类型的返回值 !!
- 接口的 抽象方法没有返回值,引用的静态方法可以有返回值 也可以没有返回值。
满足 抽象参数列表 与 引用参数列表相同,可以写成静态方法引用的格式 类名::静态方法名
案例1:
public interface Itransfer {
Integer transfor(String str);
}
public class test01 {
public static void main(String[] args) {
//------------------------匿名内部类---------------------
Itransfer itransfer = new Itransfer() {
@Override
public Integer transfor(String str) {
return Integer.valueOf(str);
}
};
System.out.println(itransfer.transfor("10"));
//------------------------lambda表达式---------------------
Itransfer itransfer1 = str -> Integer.valueOf(str);
System.out.println(itransfer1.transfor("50")+20);
//------------------------方法引用---------------------
Itransfer itransfer2= Integer::valueOf;
itransfer.transfor("20");
}
}
5.2. 实例方法引用【掌握】(***)
语法:类名:: 非静态方法名
注意事项:
- 被 引用的实例方法 参数列表 和 函数式接口中抽象方法 的参数一致。
- 接口的抽象方法没有返回值,引用的实例方法可以有返回值也可以没有
3. 接口的抽象方法有返回值,引用的实例方法必须有相同类型的返回值!!
public class Person {
public String say(String words){
return "老王头总是说"+words;
}
}
@FunctionalInterface
public interface ISpeek {
String speekOne(String str);
}
public class test01 {
public static void main(String[] args) {
//------------------------匿名内部类---------------------
ISpeek iSpeek = new ISpeek() {
@Override
public String speekOne(String str) {
return new Person().say(str);
}
};
System.out.println(iSpeek.speekOne("你好"));
//------------------------lambda表达式---------------------
ISpeek iSpeek1= str -> new Person().say(str);
System.out.println(iSpeek.speekOne("hhhh"));
//------------------------方法引用---------------------
ISpeek iSpeek2 = new Person()::say;
System.out.println(iSpeek2.speekOne("方法引用调用的"));
}
5.3. 构造方法引用【掌握】
语法:类名 :: new
注意事项:
- 构造方法 与 函数式接口的抽象方法 参数列表一致。
public class Person {
private String name;
private Integer age;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public Person() {
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public interface IAnimal {
Person create(String name,Integer age);
}
public class test01 {
public static void main(String[] args) {
//------------------------匿名内部类---------------------
IAnimal iAnimal = new IAnimal() {
@Override
public Person create(String name, Integer age) {
return new Person(name,age);
}
};
Person person = iAnimal.create("小可", 22);
System.out.println(person);
//------------------------lambda表达式---------------------
IAnimal animal = (name,age)-> new Person(name,age);
System.out.println(animal.create("小小",90));
//------------------------方法引用---------------------
IAnimal iAnimal1 = Person::new; //构造方法
Person person1 = iAnimal.create("小曹", 21);
System.out.println(person1);
}
}
lambda 中调用的 变量 必须用final修饰。
6. Stream流
6.1. Stream 流的引入
在 Java8 之前,通常用 fori、for each 或者 Iterator 迭代来重排序合并数据,或者通过重新定义 Collections.sorts的 Comparator 方法来实现,这两种方式对 大数量系统来说,效率不理想。
Java8 中添加了一个新的接口类 Stream,相当于高级版的 Iterator,通过Lambda 表达式对集合进行各种非常便利、高效的聚合操作(Aggregate Operation),或者大批量数据操作 (Bulk Data Operation)。
Stream不仅可以通过串行的方式实现数据操作,还可以通过并行的方式处理大批量数据,提高数据的处理效率。
6.2. 什么是Stream流
Stream(流)是一个来自数据源的元素队列并支持聚合操作。
- 数据源 流的来源。 可以是集合、数组,I/O channel(nio new IO非阻塞式IO), 产生器generator 等。
- 聚合操作 类似SQL语句一样的操作,比如fiter、map、reduce、find、match、sorted等。
- Pipelining:中间操作都会返回对象本身。
- 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式,显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。
6.2.1. Stream流的使用
- 生成流
通过数据源(集合、数组等)生成流。list.stream()
- 中间操作
一个流后面可以跟随零个或多个中间操作,其目的主要是打开流,做出某种程度的数据过滤/映射,然后返回一个新的流,交给下一个操作。filter()
- 终结操作
一个流只能有一个终结操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。forEach();
6.3. 生成流
在 Java 8 中, 集合接口有两个方法来生成流:
Stream() 是 Collection 类中的方法。
- stream() : 为集合创建串行流。
- parallelStream() : 为集合创建并行流
List<Integer> list = Arrays.asList(5, 2, 3, 1, 6, 10, 9);
list.stream();
6.4. Stream流的常见中间操作方法
6.4.1. filter 对流中数据过滤
Stream filter(Predicate predicate): 用于对流中的数据进行过滤。
Predicate 接口中的方法 boolean test(T t):对给定的参数进行判断,返回一个布尔值
List<String> list = new ArrayList<>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
list.add("张敏");
list.add("张无忌");
//需求1 :把list集合中以张开头的元素在控制台输出
list.stream().filter(s -> s.startsWith("张")).forEach(System.out::println);
System.out.println("-----");
//需求2:把list集合中长度为3的元素在控制台输出
list.stream().filter(s -> s.length()==3).forEach(System.out::println);
System.out.println("------");
//需求3:把list集合中以张开头,长度为3的元素在控制台输出
list.stream().filter(s -> s.startsWith("张")).filter(s ->
s.length()==3).forEach(System.out::println);
6.4.2. map & mapTolnt -->从流中获取数据;。
Stream map(Function mapper) : 返回由该流的 元素 组成的流。
map 方法用于映射每个元素到对应的结果。
Function接口中的方法 R apply(T t)
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
// 获取对应的平方数
numbers.stream().map( i -> i*i).forEach(System.out::println);
InStream mapTolnt(TolntFunction mapper) : 返回一个IntStream其中包含给定函数应用于此流的元素的结果。
IntStream: 表示元素int流
TolntFunction接口的方法 int applyAslnt(T value)
//创建一个集合,存储多个字符串元素
ArrayList<String> list = new ArrayList<>();
list.add("10");
list.add("20");
list.add("30");
list.add("40");
list.add("50");
//需求:将集合中的字符串数据转换为整数之后在控制台输出
list.stream().map(s -> Integer.parseInt(s)).forEach(System.out::println);
list.stream().map(Integer::parseInt).forEach(System.out::println);
//InStream mapTolnt(TolntFunction mapper) : 返回一个IntStream其中包含给定函
数应用于此流的元素的结果
//int applyAslnt(T value)
//InStream中的方法 int sum():返回此流中元素的总和
list.stream().mapToInt(Integer::parseInt).forEach(System.out::println);
int sum = list.stream().map
6.4.3. limit
Stream limit(long maxSize): 方法用于获取指定数量的流。
Random random = new Random();
//从随机流中获取十个随机数:并输出结果
random.ints().limit(10).forEach(System.out::println);
6.4.4. skip
Steam skip(long n):跳过指定参数个数的数据,返回由该流剩余元素组成的流
//创建一个集合,存储多个字符串元素
ArrayList<String> list = new ArrayList<>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
list.add("张敏");
list.add("张无忌");
//需求1: 取前三个数据在控制台输出
list.stream().limit(3).forEach(System.out::println);
System.out.println("--");
//需求2:跳过3个元素,把剩下的元素在控制台输出
list.stream().skip(3).forEach(System.out::println);
//需求3:跳过2个元素,把剩下的元素中前2个在控制台输出
list.stream().skip(2).limit(2).forEach(System.out::println);
6.4.5. concat
static Stream concat(Stream a,Stream b):合并a和b两个流为一个流。
6.4.6. distinct
Stream distinct() :返回由该流的不同元素(根据Object.equals(Object))组成流(去重复)
//创建一个集合,存储多个字符串元素
ArrayList<String> list = new ArrayList<>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
list.add("张敏");
list.add("张无忌");
//需求1:取前4个数据组成一个流
Stream<String> limit = list.stream().limit(4);
//需求2:跳过数据组成一个流
Stream<String> skip = list.stream().skip(2);
//需求3:合并需求1和需求2得到的流,并把结果在控制台输出
Stream.concat(limit,skip).forEach(System.out::println);
6.4.7. sorted
Stream sorted: 返回由此流的元素组成的流,根据自然顺序排序。
Stream sorted(Comparator comparator) : 返回由该流的元素组成的流,根据提供的Comparator进行排序。
//创建一个集合,存储多个字符串元素
ArrayList<String> list=new ArrayList<>();
list.add("lingqingxia");
list.add("zhangmanyu");
list.add("liuyan");
list.add("zhangmin");
list.add("zhangwuji");
//需求1:按照字母顺序把数据在控制台输出
list.stream().sorted().forEach(System.out::println);
//需求2:按照字符串长度把数据在控制台输出
//list.stream().sorted((s1,s2)->s1.length()-
s2.length()).forEach(System.out::println);
//长度相同按照自然顺序排序
list.stream().sorted((s1,s2)->{
int num=s1.length()-s2.length();
int num2=num==0?s1.compareTo(s2):num;
return num2;
});
6.5. Stream 流的常见终结操作方法
- void forEach(Consumer action): 对此流的每个元素执行操作。
Consumer接口中的方法 void accept(T t):对给定的参数执行此操作
- long count():返回此流中的元素数
ArrayList<String> list = new ArrayList<>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
list.add("张敏");
list.add("张无忌");
//需求1:把集合中的元素在控制台输出
list.stream().forEach(s -> System.out.println(s));
list.stream().forEach(System.out::println);
//需求2: 统计集合中有几个以张开头的元素,并把统计结果在控制台输出
long count = list.stream().filter(s -> s.startsWith("张")).count();
System.out.println(count);
6.6. Stream流的收集操作
对数据使用Stream流的方式操作完毕后,我想把流中的数据收集到集合中,该怎么办呢?
6.6.1. Stream流的收集方法
R collect(Collector collector)
但是这个收集方法的参数是一个Collector接口
工具类Collectors提供了具体的收集方式
6.6.2. Collector toList()
public static Collector toList(): 把元素收集到List集合中
//创建list集合对象
List<String> list=new ArrayList<>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("张曼玉");
list.add("柳岩");
//需求1:得到名字为3个字的流
Stream<String> listStream = list.stream().filter(s -> s.length() == 3);
//需求2:把使用Stream流操作完毕的数据收集到list集合中并遍历
List<String> names = listStream.collect(Collectors.toList());
for (String name:names) {
System.out.println(name);
}
6.6.3. Collector toSet()
public static Collector toSet(): 把元素收集到set集合中。
//创建set集合对象
Set<Integer> set = new HashSet<>();
set.add(10);
set.add(20);
set.add(30);
set.add(33);
//需求3:得到年龄大于25的流
Stream<Integer> setStream = set.stream().filter(s -> s > 25);
//需求4:把使用Stream流操作完毕的数据收集到set集合中并遍历
Set<Integer> ages = setStream.collect(Collectors.toSet());
for (Integer age:ages ) {
System.out.println(age);
}
6.6.4. Collector toMap
public static Collector toMap(Function keyMapper,Function valueMapper) :把元素收集到map集合中
//定义一个字符串数组,每一个字符串数据由姓名数据和年龄数据组合而成
String[] strArray={"林青霞,30","张曼玉,35","王祖贤,33","柳岩,35"};
//需求5:得到字符串中年龄数据大于28的流
Stream<String> arrayStream = Stream.of(strArray).filter(s ->
Integer.parseInt(s.split(",")[1]) > 28);
//需求6:把使用Stream流操作完毕的数据收集到Map集合中并遍历,字符串中的姓名做
键,年龄做值
Map<String,Integer> map = arrayStream.collect(Collectors.toMap(s ->
s.split(",")[0], (String s) -> {
return Integer.parseInt(s.split(",")[1]);
}
));
for ( String key:map.keySet()) {
Integer value=map.get(key);
System.out.println(key+","+value);
}
7. Optional类
主要用于判空
- Optional 类是一个可以为null的容器对象。
- 如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
- Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。
- Optional 类的引入很好的解决空指针异常
7.1. Optional常用方法
- Optional.of(T t):创建要给Optional实例
- Optional.empty():创建一个空的Optional实例。
- Optional.ofNullable(T t):若t不为null,创建Optional实例,否则创建空的实例。
- isPresent():判断是否包含值。
- orElse(T t):如果调用对象包含值,返回该值,否则返回t。
- orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回s获取的值。
- map(function f):如果有值对其处理,并发挥处理后的Optional ,否则返回Optional.empty()。
- flatMap(Function mapper):与map类似,要求返回值必须