尚硅谷JavaSE笔记合集
一、Java8概述
- Java 8 (又称为jdk 1.8) 是Java 语言开发的一个主要版本。
- Java 8 是oracle公司于2014年3月发布,可以看成是自Java 5 以来最具革命性的版本。Java 8为Java语言、编译器、类库、开发工具与JVM带来了大量新特性。
![在这里插入图片描述](https://img-blog.csdnimg.cn/468f9d7744124869a21fbe531c7a08f9.png#pic_center)
二、Java8新特性的好处
- 速度更快
- 代码更少(增加了新的语法:Lambda 表达式)
- 强大的Stream API
- 便于并行
- 最大化减少空指针异常:Optional
- Nashorn引擎,允许在JVM上运行JS应用
三、并行流与串行流
并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。相比较串行的流,并行的流可以很大程度上提高程序的执行效率。
Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream API 可以声明性地通过parallel() 与sequential() 在并行流与顺序流之间进行切换。
四、Lambda表达式
4.1 介绍
Lambda 是一个匿名函数,我们可以把Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
总结:作为函数式接口的匿名实现类的实现
-
lambda形参列表:
- 参数类型可以省略(类型推断)
- 只有一个参数,()也可以省略
-
lambda体:应该使用一对{}包裹
- 只有一条执行语句,省略{}
- 只有return语句,省略return关键字
4.2 语法
//语法格式一:无参,无返回值
Runnable r2 = () -> System.out.println("长安欢迎您");
//语法格式二:一个参数,没有返回值。
Consumer<String> c1 = (String s) -> {
System.out.println(s);
};
c1.accept("先天人性无善恶,后天人性有善恶。");
//语法格式三:因为可由编译器推断得出(“类型推断”),数据类型可以省略,
Consumer<String> c2 = (s) -> {
System.out.println(s);
};
c2.accept("如果没有邪恶的话我们怎么会知道人世间的那些善良呢?");
//语法格式四:只需要一个参数时,小括号可以省略
Consumer<String> c2 = s -> {
System.out.println(s);
};
c2.accept("如果没有邪恶的话我们怎么会知道人世间的那些善良呢?");
//语法格式五:需要两个或以上的参数,多条执行语句,并且可以有返回值
Comparator<Integer> com2 = (o1,o2) -> {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
System.out.println(com2.compare(16,8));
//语法格式六:Lambda体只有一条语句时,return与大括号都可以省略
Comparator<Integer> c2 = (o1,o2) -> o1.compareTo(o2);
System.out.println(c2.compare(17,24));
五、函数式接口
5.1、介绍
/*
* 4.Lambda表达式的本质:作为函数式接口的实例
*/
/**
* 自定义函数式接口
*/
@FunctionalInterface //可以使用 @FunctionalInterface 检查它是否是一个函数式接口
public interface MyInterFace {
void method(); //接口中,只声明了一个抽象方法
}
- 在java.util.function包下定义了Java 8 的丰富的函数式接口
- Java从诞生日起就是一直倡导“一切皆对象”,在Java里面面向对象(OOP)编程是一切。但是随着python、scala等语言的兴起和新技术的挑战,Java不得不做出调整以便支持更加广泛的技术要求,也即java不但可以支持OOP还可以支持OOF(面向函数编程)
- 在函数式编程语言当中,函数被当做一等公民对待。在将函数作为一等公民的编程语言中,Lambda表达式的类型是函数。但是在Java8中,有所不同。在Java8中,Lambda表达式是对象,而不是函数,它们必须依附于一类特别的对象类型——函数式接口。
- 简单的说,在Java8中,Lambda表达式就是一个函数式接口的实例。这就是Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口的实例,那么该对象就可以用Lambda表达式来表示。
- 所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。
5.2、内置函数式接口
函数式接口 |
参数类型 |
返回类型 |
用途 |
Consumer 消费型接口 |
T |
void |
对类型为T的对象应用操作,包含方法:void accept(T t)
|
Supplier 供给型接口 |
无 |
T |
返回类型为T的对象,包含方法:T get()
|
Function<T, R> 函数型接口 |
T |
R |
对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t)
|
Predicate 断定型接口 |
T |
boolean |
确定类型为T的对象是否满足某约束,并返回boolean 值。包含方法:boolean test(T t)
|
BiFunction<T,U,R> |
T, U |
R |
对类型为T,U参数应用操作,返回R类型的结果。包含方法为:Rapply(T t,U u) ; |
UnaryOperator (Function子接口) |
T |
T |
对类型为T的对象进行一元运算,并返回T类型的结果。包含方法为:Tapply(T t) ; |
BinaryOperator (BiFunction子接口) |
T,T |
T |
对类型为T的对象进行二元运算,并返回T类型的结果。包含方法为:Tapply(T t1,T t2) ; |
BiConsumer<T,U> |
T,U |
void |
对类型为T,U参数应用操作。包含方法为:voidaccept(Tt,Uu)
|
BiPredicate<T,U> |
T,U |
boolean |
包含方法为:booleantest(Tt,Uu)
|
ToIntFunction |
T |
int |
计算int 值的函数 |
ToLongFunction |
T |
long |
计算long 值的函数 |
ToDoubleFunction |
T |
double |
计算double 值的函数 |
IntFunction |
int |
R |
参数为int 类型的函数 |
LongFunction |
long |
R |
参数为long 类型的函数 |
DoubleFunction |
double |
R |
参数为double 类型的函数 |
/**
* java内置的4大核心函数式接口
*
* 消费型接口 Consumer<T> void accept(T t) 有参数无返回值
* 供给型接口 Supplier<T> T get() 无参数有返回值
* 函数型接口 Function<T,R> R apply(T t) 有参数有返回值
* 断定型接口 Predicate<T> boolean test(T t) 有参数返回真假
*/
六、方法、构造器、数组引用
6.1 介绍
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。
- 格式:类(或对象)
::
方法名
- 主要情况:
- 对象 :: 非静态方法 【函数式接口中定义方法的形参列表和返回值类型与方法引用相同时可使用】
- 类 :: 静态方法 【函数式接口中定义方法的形参列表和返回值类型与方法引用相同时可使用】
- 类 :: 非静态方法 【函数式接口中定义方法的第一个参数作为方法引用中的类,其他参数作为形参列表或返回值类型】
6.2 方法引用
-
对象::实例方法
@Test
public void demo1(){
String str="abc";
/*Consumer<String> consumer=(s)->{
System.out.println(s);
};*/
Consumer<String> consumer=System.out::println;
Supplier<Integer> supplier=str::length;
Function<Integer,String> function=str::substring;
Predicate<String> predicate=str::endsWith;
consumer.accept("方法引用之实例::实例方法(Consumer)");
consumer.accept("方法引用之实例::实例方法(Supplier):"+supplier.get());
consumer.accept("方法引用之实例::实例方法(Function):"+function.apply(0));
consumer.accept("方法引用之实例::实例方法(Predicate):"+predicate.test("c"));
}
-
类::类方法
@Test
public void demo2(){
List<String> list= Arrays.asList("javase","hello","word");
List<String> list2= Arrays.asList("javase","hello");
Consumer<List<String>> consumer= Collections::sort;
Supplier<Iterator> supplier=Collections::emptyIterator;
Function<List<String>,List<String>> function=Collections::synchronizedList;
BiPredicate<List<String>,List<String>> predicate=Collections::disjoint;
consumer.accept(list);
log.debug("方法引用之类::类方法(Consumer):"+list);
log.debug("方法引用之类::类方法(Supplier):"+supplier.get());
log.debug("方法引用之类::类方法(Function):"+function.apply(list));
log.debug("方法引用之类::类方法(Predicate):"+predicate.test(list,list2));
}
-
类::实例方法
@Test
public void demo3(){
ArrayList<String> list= new ArrayList<>(Arrays.asList("javase","hello","word"));
BiConsumer<List<String>,Consumer> consumer= List::forEach;
BiFunction<List<String>,Object,Integer> function=List::indexOf;
BiPredicate<List<String>,String> predicate=List::contains;
Integer javase = function.apply(list, "javase");
log.debug("方法引用之类::实例方法(Consumer):");
consumer.accept(list,System.out::println);
log.debug("方法引用之类::实例方法(Function):"+javase);
log.debug("方法引用之类::实例方法(Predicate):"+predicate.test(list,"javase"));
}
6.5 构造器引用
和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。
抽象方法的返回值类型即为构造器所属的类的类型
- 与函数式接口相结合,自动与函数式接口中方法兼容。
- 格式:ClassName::new
@Test
public void demo4(){
Supplier<List> supplier=ArrayList::new;
Function<Integer,Integer> function=Integer::new;
Predicate<Boolean> predicate=Boolean::new;
System.out.println(supplier.get());
System.out.println(function.apply(1));
System.out.println(predicate.test(true));
}
6.6 数组引用
可以把数组看做是一个特殊的类,则写法与构造器引用一致。
@Test
public void demo5(){
Function<Integer,Integer[]> function=Integer[]::new;
System.out.println(function.apply(2));
}
七、Stream API
7.1 概述
- Java8中有两大最为重要的改变。第一个是Lambda 表达式;另外一个则是Stream API。
- Stream API (
java.util.stream
)把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充,因为Stream API可以极大提供Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
- Stream 是Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用SQL 执行的数据库查询。也可以使用Stream API 来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
-
为什么要使用Stream API
-
实际开发中,项目中多数数据源都来自于Mysql,Oracle等。但现在数据源可以更多了,有MongDB,Radis等,而这些NoSQL的数据就需要Java层面去处理。
-
Stream 和Collection 集合的区别:Collection 是一种静态的内存数据结构,而Stream 是有关计算的。前者是主要面向内存,存储在内存中,后者主要是面向CPU,通过CPU 实现计算。
/**
* 1.Stream关注的是对数据的运算,与CPU打交道
* 集合关注的是数据的存储,与内存打交道
*
* 2.特点
* ①Stream 自己不会存储元素。
* ②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
* ③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行
*
* 3.Stream 执行流程
* ① Stream的实例化
* ② 一系列的中间操作(过滤、映射、...)
* ③ 终止操作
*
* 4.说明:
* 4.1 一个中间操作链,对数据源的数据进行处理
* 4.2 一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用
*/
![在这里插入图片描述](https://img-blog.csdnimg.cn/dd637dbb5b4543e09f6baf1deb891eff.png#pic_center)
7.2 创建
-
1、通过 java.util.Collection.stream()/parallelStream() 方法用集合创建流
stream():创建顺序流。
parallel():把顺序流转换成并行流
parallelStream():创建并行流。内部以多线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求
2、使用java.util.Arrays.stream(T[] array)方法用数组创建流
3、使用Stream的静态方法创建流:
- of(1,2,3,4) //有限流
- iterate(0, (x) -> x + 3).limit(4) //无限流
- generate(Math::random).limit(3) //无限流
-
public class StreamAPITest {
//创建 Stream方式一:通过集合
@Test
public void test(){
List<Employee> employees = EmployeeData.getEmployees();
// default Stream<E> stream() : 返回一个顺序流
Stream<Employee> stream = employees.stream();
// default Stream<E> parallelStream() : 返回一个并行流
Stream<Employee> parallelStream = employees.parallelStream();
}
//创建 Stream方式二:通过数组
@Test
public void test2(){
int[] arr = new int[]{1,2,3,4,5,6};
//调用Arrays类的static <T> Stream<T> stream(T[] array): 返回一个流
IntStream stream = Arrays.stream(arr);
Employee e1 = new Employee(1001,"Hom");
Employee e2 = new Employee(1002,"Nut");
Employee[] arr1 = new Employee[]{e1,e2};
Stream<Employee> stream1 = Arrays.stream(arr1);
}
//创建 Stream方式三:通过Stream的of()
@Test
public void test3(){
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
}
//创建 Stream方式四:创建无限流
@Test
public void test4(){
// 迭代
// public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
//遍历前10个偶数
Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);
// 生成
// public static<T> Stream<T> generate(Supplier<T> s)
Stream.generate(Math::random).limit(10).forEach(System.out::println);
}
}
7.3 中间操作
-
中间操作
过滤、筛选与切片
- filter(Predicate p):从流中排除某些元素
- distinct():去重
- limit(long maxSize):取前maxSize个元素
- skip(long n):跳过前n个元素
映射
- map(Function f):接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
- flatMap(Function f):接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
排序
- sorted():自然排序,流中元素需实现Comparable接口
- sorted(Comparator com):Comparator排序器自定义排序
-
public static final <T> Stream<T> getStream(ArrayList<T> list){
return list.stream();
}
/**
* 中间操作
* 终止操作-遍历:forEach
*/
@Test
public void demo4(){
Stream<String> stream = getStream(new ArrayList<String>(Arrays.asList("aaa","bbb","ccc")));
//中间操作:映射->排序->限制->切片->去重->过滤
Stream<String> stringStream = stream
.map(item -> item + item)
.sorted().limit(3)
.skip(0)
.distinct()
.filter(item -> item instanceof String);
//终止操作
stringStream.forEach(System.out::println);
}
7.4 终止操作
-
终止操作:从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是void 。
遍历
- forEach(Consumer c):内部迭代
匹配与查找
- allMatch(Predicate p): 全部元素是否都符合条件
- anyMatch(Predicate p):是否包含一个符合条件的元素
- noneMatch(Predicate p):是否没有包含符合条件的元素
- findFirst():匹配第一个
- findAny():匹配任意(适用于并行流)
- count():统计流中个数
- max(Comparator c):返回流中最大值
- min(Comparator c):返回流中最小值
归约
- reduce(T iden, BinaryOperator b):归约/聚合,返回T。
- reduce(BinaryOperator b):归约/聚合,返回Optional。
收集:
collect(Collectors.toList())把一个流收集起来,最终可以是收集成一个值也可以收集成一个新的集合。
- toList()
- toSet()
- toMap(Person::getName, p -> p)
- 计数:counting
- 平均值:averagingInt、averagingLong、averagingDouble
- 最值:maxBy、minBy
- 求和:summingInt、summingLong、summingDouble
- 统计以上所有:summarizingInt、summarizingLong、summarizingDouble
- partitioningBy:分区。按条件分为两个Map
- groupingBy:分组。按参数值分为多个Map
- joining(","):将stream中元素用特定的连接符(没有的话,则直接连接)连接成一个字符串
- reducing:相比于stream本身的reduce方法,增加了对自定义归约的支持。
-
/**
* 终止操作:从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是void 。
* 匹配与查找
*/
@Test
public void demo5(){
Stream<String> stream = getStream(new ArrayList<String>(Arrays.asList("aaa", "bbb", "ccc")));
//匹配与查找
// boolean b = stream.allMatch(item -> item instanceof String);
// log.debug("匹配与查找allMatch:"+b);//true
// boolean aaa = stream.anyMatch(item -> item.equals("aaa"));
// log.debug("匹配与查找anyMatch:"+aaa);//true
// boolean ddd = stream.noneMatch(item -> item.equals("ddd"));
// log.debug("匹配与查找noneMatch:"+ddd);//true
// Optional<String> any = stream.parallel().findAny();
// String s = any.get();
// log.debug("匹配与查找findAny:"+s);//bbb
// long count = stream.count();
// log.debug("匹配与查找count:"+count);//3
// Optional<String> max = stream.max((a, b) -> a.compareTo(b));
// String s = max.get();
// log.debug("匹配与查找max:"+s);//ccc
Optional<String> min = stream.min((a, b) -> a.compareTo(b));
String s = min.get();
log.debug("匹配与查找min:"+s);//aaa
}
/**
* 终止操作:从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是void 。
* 归约
*/
@Test
public void demo6(){
Stream<String> stream = getStream(new ArrayList<String>(Arrays.asList("aaa", "bbb", "ccc")));
// String hello_ = stream.reduce("hello ", (a, b) -> a + b);
// log.debug("归约reduce:"+hello_);//hello aaabbbccc
Optional<String> reduce = stream.reduce((a, b) -> a + b);
String s = reduce.get();
log.debug("归约reduce:"+s);//aaabbbccc
}
/**
* 终止操作:从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是void 。
* 收集
*/
@Test
public void demo7(){
Stream<String> stream = getStream(new ArrayList<String>(Arrays.asList("aaa", "bbb", "ccc")));
// List<String> collect = stream.collect(Collectors.toList());
// log.debug("收集Collectors.toList:"+collect.toString());//[aaa, bbb, ccc]
// Set<String> collect1 = stream.collect(Collectors.toSet());
// log.debug("收集Collectors.toSet:"+collect1.toString());//[aaa, ccc, bbb]
// Map<String, String> collect = stream.collect(Collectors.toMap(item -> item, item -> item));
// log.debug("收集Collectors.toMap:"+collect.toString());//{aaa=aaa, ccc=ccc, bbb=bbb}
// Long collect = stream.collect(Collectors.counting());
// log.debug("收集Collectors.counting:"+collect);//3
// Optional<String> collect = stream.collect(Collectors.maxBy((a, b) -> a.compareTo(b)));
// String s = collect.get();
// log.debug("收集Collectors.maxBy:"+s);//ccc
// Optional<String> collect = stream.collect(Collectors.minBy((a, b) -> a.compareTo(b)));
// String s = collect.get();
// log.debug("收集Collectors.minBy:"+s);//aaa
// Map<Boolean, List<String>> aaa = stream.collect(Collectors.partitioningBy(item -> item.equals("aaa")));
// log.debug("收集Collectors.partitioningBy:"+aaa.toString());//{false=[bbb, ccc], true=[aaa]}
// Map<String, List<String>> collect = stream.collect(Collectors.groupingBy(item -> item));
// log.debug("收集Collectors.groupingBy:"+collect.toString());//{aaa=[aaa], ccc=[ccc], bbb=[bbb]}
// String collect1 = stream.collect(Collectors.joining(" "));
// log.debug("收集Collectors.joining:"+collect1);//aaa bbb ccc
Optional<String> collect = stream.collect(Collectors.reducing((a, b) -> a + b));
String s = collect.get();
log.debug("收集Collectors.reducing:"+s);//aaabbbccc
}
八、Optional类
8.1、Optional类的介绍
到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google Guava的启发,Optional类已经成为Java 8类库的一部分。
-
Optional
类(java.util.Optional
) 是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null
,表示这个值不存在。原来用null
表示一个值不存在,现在Optional
可以更好的表达这个概念。并且可以避免空指针异常。
- Optional类的Javadoc描述如下:这是一个可以为null的容器对象。如果值存在则
isPresent()
方法会返回true
,调用get()
方法会返回该对象。
- Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
- 创建Optional类对象的方法:
-
Optional.of(T t)
: 创建一个Optional 实例,t必须非空;通常与get()配合使用
-
Optional.empty()
: 创建一个空的Optional 实例
-
Optional.ofNullable(T t
):t可以为null。通常与orElse()配合使用
- 判断Optional容器中是否包含对象:
-
boolean isPresent()
: 判断是否包含对象
-
void ifPresent(Consumer<? super T> consumer)
:如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它。
- 获取Optional容器的对象:
-
T get()
: 如果调用对象包含值,返回该值,否则抛异常。通常与of()配合使用
-
T orElse(T other)
:如果有值则将其返回,否则返回指定的other对象。通常与ofNullable()配合使用
-
T orElseGet(Supplier<? extends T> other)
:如果有值则将其返回,否则返回由Supplier接口实现提供的对象。
-
T orElseThrow(Supplier<? extends X> exceptionSupplier)
:如果有值则将其返回,否则抛出由Supplier接口实现提供的异常。
1、Boy类
public class Boy {
private Girl girl;
public Boy() {
}
public Boy(Girl girl) {
this.girl = girl;
}
public Girl getGirl() {
return girl;
}
public void setGirl(Girl girl) {
this.girl = girl;
}
@Override
public String toString() {
return "Boy{" +
"girl=" + girl +
'}';
}
}
2、Girl类
public class Girl {
private String name;
public Girl() {
}
public Girl(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Girl{" +
"name='" + name + '\'' +
'}';
}
}
3、测试类
import org.junit.Test;
import java.util.Optional;
/**
* Optional类:为了在程序中避免出现空指针异常而创建的。
*
* 常用的方法:ofNullable(T t)
* orElse(T t)
*/
public class OptionalTest {
/**
* Optional.of(T t) : 创建一个 Optional 实例,t必须非空;
* Optional.empty() : 创建一个空的 Optional 实例
* Optional.ofNullable(T t):t可以为null
*/
@Test
public void test(){
Girl girl = new Girl();
// girl = null;
//of(T t):保证t是非空的
Optional<Girl> optionalGirl = Optional.of(girl);
}
@Test
public void test2(){
Girl girl = new Girl();
// girl = null;
//ofNullable(T t):t可以为null
Optional<Girl> optionalGirl = Optional.ofNullable(girl);
System.out.println(optionalGirl);
//orElse(T t1):如果单前的Optional内部封装的t是非空的,则返回内部的t.
//如果内部的t是空的,则返回orElse()方法中的参数t1.
Girl girl1 = optionalGirl.orElse(new Girl(""));
System.out.println(girl1);
}
}
8.2、Optional类的使用举例
1、测试类
import org.junit.Test;
import java.util.Optional;
/**
* Optional类:为了在程序中避免出现空指针异常而创建的。
*
* 常用的方法:ofNullable(T t)
* orElse(T t)
*/
public class OptionalTest {
@Test
public void test3(){
Boy boy = new Boy();
boy = null;
String girlName = getGirlName(boy);
System.out.println(girlName);
}
private String getGirlName(Boy boy) {
return boy.getGirl().getName();
}
//优化以后的getGirlName():
public String getGirlName1(Boy boy){
if(boy != null){
Girl girl = boy.getGirl();
if(girl != null){
return girl.getName();
}
}
return null;
}
@Test
public void test4(){
Boy boy = new Boy();
boy = null;
String girlName = getGirlName1(boy);
System.out.println(girlName);
}
//使用Optional类的getGirlName():
public String getGirlName2(Boy boy){
Optional<Boy> boyOptional = Optional.ofNullable(boy);
//此时的boy1一定非空
Boy boy1 = boyOptional.orElse(new Boy(new Girl("朱淑贞")));
Girl girl = boy1.getGirl();
Optional<Girl> girlOptional = Optional.ofNullable(girl);
//girl1一定非空
Girl girl1 = girlOptional.orElse(new Girl("阿青"));
return girl1.getName();
}
@Test
public void test5(){
Boy boy = null;
boy = new Boy();
boy = new Boy(new Girl("李清照"));
String girlName = getGirlName2(boy);
System.out.println(girlName);
}
}