Stream API(java.util.stream)把真正的函数式编程风格引入到Java中,可以极大地提高程序员生产力,让程序员写出高效、简洁的代码
实际开发中项目中多数数据源都是来自MySQL、Oracle等关系型数据库,还有部分来自MongDB、Redis等非关系型数据库
Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合/数组进行的操作(可是是非常复杂的查找、过滤和映射数据等操作)
Stream和Collection集合的区别: Stream关注的是数据的运算(过滤操作)与CPU打交道, 集合关注的是数据的存储(静态的存储结构)与内存打交道
Stream的特性
public class Employee {
private int id;
private String name;
private int age;
private double salary;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
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 double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public Employee() {
System.out.println("Employee().....");
}
public Employee(int id) {
this.id = id;
System.out.println("Employee(int id).....");
}
public Employee(int id, String name) {
this.id = id;
this.name = name;
}
public Employee(int id, String name, int age, double salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
@Override
public String toString() {
return "Employee{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", salary=" + salary + '}';
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Employee employee = (Employee) o;
if (id != employee.id)
return false;
if (age != employee.age)
return false;
if (Double.compare(employee.salary, salary) != 0)
return false;
return name != null ? name.equals(employee.name) : employee.name == null;
}
@Override
public int hashCode() {
int result;
long temp;
result = id;
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + age;
temp = Double.doubleToLongBits(salary);
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
}
提供用于测试数据的类
public class EmployeeData {
public static List<Employee> getEmployees() {
List<Employee> list = new ArrayList<>();
list.add(new Employee(1001, "马化腾", 34, 6000.38));
list.add(new Employee(1002, "马云", 12, 9876.12));
list.add(new Employee(1003, "刘强东", 33, 3000.82));
list.add(new Employee(1004, "雷军", 26, 7657.37));
list.add(new Employee(1005, "李彦宏", 65, 5555.32));
list.add(new Employee(1006, "比尔盖茨", 42, 9500.43));
list.add(new Employee(1007, "任正非", 26, 4333.32));
list.add(new Employee(1008, "扎克伯格", 35, 2500.32));
return list;
}
}
方式一通过集合:Java8中的Collection接口扩展了两个获取Stream流的方法
方法名 | 功能 |
---|---|
default Stream stream() | 返回一个顺序流(按照顺序操作元素) |
default Stream parallelStream() | 返回一个并行流(并发的操作元素) |
@Test
public void test01(){
List<Integer> list = Arrays.asList(1,2,3,4,5);
// 返回一个顺序流
Stream<Integer> stream1 = list.stream();
// 返回一个并行流
Stream<Integer> stream2 = list.parallelStream();
List<Employee> employees = EmployeeData.getEmployees();
// 返回一个顺序流
Stream<Employee> stream = employees.stream();
// 返回一个并行流
Stream<Employee> employeeStream = employees.parallelStream();
}
方式二通过数组: Java8中的Arrays的静态方法stream()可以获取对应类型的Stream流
方法名 | 功能 |
---|---|
static Stream stream(T[] array) | 返回一个自定义类型的Stream流 |
public static IntStream stream(int[] array) | 返回一个int类型的Stream流 |
public static LongStream stream(long[] array) | 返回一个long类型的Stream流 |
public static DoubleStream stream(double[] array) | 返回一个double类型的Stream流 |
@Test
public void test(){
// 获取基本数据类型的Stream流
int[] arr = {1,2,3,4,5};
IntStream stream = Arrays.stream(arr);
// 获取引用数据类型的Stram流
String[] arr = {"hello","world"};
Stream<String> stream = Arrays.stream(arr);
// 获取自定义类型的Stream流
Employee kyle = new Employee(9527, "Kyle");
Employee lucy = new Employee(9421, "Lucy");
Employee[] employees = {kyle, lucy};
Stream<Employee> stream1 = Arrays.stream(employees);
}
方式三通过指定具体数据: 调用Stream类静态方法of()创建一个流
方法名 | 功能 |
---|---|
public static Stream of(T… values) | 通过手动指定任意个数据创建一个流 |
@Test
public void test04(){
Stream<Integer> stream = Stream.of(1,2,3,4,5);
stream.forEach(System.out::println);
}
方式四调用Stream类的静态方法iterate()和generate()创建无限流(了解)
方法名 | 功能 |
---|---|
public static Stream iterate(final T seed, final UnaryOperator f) | 迭代 |
public static Stream generate(Supplier s) | 生成 |
@Test
public void test() {
// 从0开始迭代遍历10个数,没有limit限制会无限迭代
Stream.iterate(0, t -> t + 1).limit(10).forEach(System.out::println);
// 生成10个随机数,没有limit限制会无限生成
Stream.generate(Math::random).limit(10).forEach(System.out::println);
}
中间操作就是处理数据的具体操作,每次处理都会返回一个持有结果的新Stream,即中间操作的方法返回值仍然是Stream类型的对象
方 法 | 描 述 |
---|---|
filter(Predicatep) | 接收Lambda表达式然后从流中排除某些元素 |
distinct() | 筛选通过流所生成元素的 hashCode() 和 equals() 去除重复元素 |
limit(long maxSize) | 截断流使其元素不超过给定数量 |
skip(long n) | 跳过元素返回一个扔掉了前 n个元素的流 若流中元素不足n个则返回一个空流,与 limit(n) 互补 |
@Test
public void test() {
List<Employee> employees = EmployeeData.getEmployees();
//1. 查询工资大于7000的员工信息
employees.stream().filter(employee -> employee.getSalary() > 7000).forEach(System.out::println);
System.out.println("----------------------------");
//2. 只输出3条员工信息
employees.stream().limit(3).forEach(System.out::println);
System.out.println("----------------------------");
//3. 跳过前3个元素
employees.stream().skip(3).forEach(System.out::println);
System.out.println("----------------------------");
//4. 通过流所生成元素的hashCode和equals方法去除重复元素
employees.add(new Employee(9527, "Kyle", 20, 9999));
employees.add(new Employee(9527, "Kyle", 20, 9999));
employees.stream().distinct().forEach(System.out::println);
}
/*
Employee{id=1002, name='马云', age=12, salary=9876.12}
Employee{id=1004, name='雷军', age=26, salary=7657.37}
Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
----------------------------
Employee{id=1001, name='马化腾', age=34, salary=6000.38}
Employee{id=1002, name='马云', age=12, salary=9876.12}
Employee{id=1003, name='刘强东', age=33, salary=3000.82}
----------------------------
Employee{id=1004, name='雷军', age=26, salary=7657.37}
Employee{id=1005, name='李彦宏', age=65, salary=5555.32}
Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
Employee{id=1007, name='任正非', age=26, salary=4333.32}
Employee{id=1008, name='扎克伯格', age=35, salary=2500.32}
----------------------------
Employee{id=1001, name='马化腾', age=34, salary=6000.38}
Employee{id=1002, name='马云', age=12, salary=9876.12}
Employee{id=1003, name='刘强东', age=33, salary=3000.82}
Employee{id=1004, name='雷军', age=26, salary=7657.37}
Employee{id=1005, name='李彦宏', age=65, salary=5555.32}
Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
Employee{id=1007, name='任正非', age=26, salary=4333.32}
Employee{id=1008, name='扎克伯格', age=35, salary=2500.32}
Employee{id=9527, name='Kyle', age=20, salary=9999.0}
*/
方法 | 描述 |
---|---|
map(Function f) | 接收一个函数作为参数,该函数会被应用到每个元素上并将其映射成一个新的元素(将元素转换成其他形式或提取信息) |
mapToDouble(ToDoubleFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上产生一个新的 DoubleStream |
mapToInt(ToIntFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上产生一个新的 IntStream |
mapToLong(ToLongFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上产生一个新的 LongStream |
flatMap(Function f) | 接收一个函数作为参数,将流中的每个值都换成另一个流然后把所有流连接成一个流, |
map和flatMap的区别: map方法最终会得到一个含有多个Stream实例的Stream,flatMap最终会得到一个含有多个元素的Stream
@Test
public void test() {
ArrayList list1 = new ArrayList();
list1.add(1);
list1.add(2);
list1.add(3);
ArrayList list2 = new ArrayList();
list2.add(1);
list2.add(2);
list2.add(3);
// 集合中有4个元素,[1,2,3,[4,5,6]],类似map
list1.add(list2);
// 集合中有6个元素,[1,2,3,4,5,6],类似flatMap
list1.addAll(list2);
System.out.println(list1);
}
public class LambdaTest{
@Test
public void test() {
List<String> strings = Arrays.asList("aa", "bb", "cc", "dd");
List<Employee> employees = EmployeeData.getEmployees();
// 将集合所有元素转为大写并输出
strings.stream().map(s -> s.toUpperCase()).forEach(System.out::println);
System.out.println("--------------------------");
// 获取员工姓名长度大于3的员工的姓名
employees.stream().map(Employee::getName).
// 过滤出name长度>3的员工
filter(name -> name.length() > 3).
// 遍历输出
forEach(System.out::println);
System.out.println("--------------------------");
// 使用map将字符串中的多个字符构成的集合转换为对应的Stream实例,然后再获取每一个Stream中的元素
strings.stream().map(LambdaTest::formStringToStream).
forEach(characterStream -> characterStream.forEach(System.out::println));
// 使用flatMap可以直接获取Stream中Stream实例中的元素
System.out.println("--------------------------");
strings.stream().flatMap(LambdaTest::formStringToStream).forEach(System.out::println);
}
public static Stream<Character> formStringToStream(String str) {
ArrayList<Character> list = new ArrayList<>();
for (char c : str.toCharArray()) {
list.add(c);
}
return list.stream();
}
}
/*
AA
BB
CC
DD
--------------------------
比尔盖茨
扎克伯格
--------------------------
a
a
b
b
c
c
d
d
--------------------------
*/
方法 | 描述 |
---|---|
sorted() | 产生一个新流其中按自然顺序排序 |
sorted(Comparator com) | 产生一个新流其中按比较器顺序排序 |
@Test
public void test20() {
List<Integer> nums = Arrays.asList(13, 54, 97, 52, 43, 64, 27);
List<Employee> employees = EmployeeData.getEmployees();
//自然排序,如果要对employees自然排序就需要Employee类实现Comparable接口
nums.stream().sorted().forEach(System.out::println);
//定制排序,先按照年龄升序排,再按照工资降序排
employees.stream().sorted((o1, o2) -> {
int compare = Integer.compare(o1.getAge(), o2.getAge());
if (compare != 0) {
return compare;
} else {
return -Double.compare(o1.getSalary(), o2.getSalary());
}
}).forEach(System.out::println);
}
/*
13
27
43
52
54
64
97
------------------
Employee{id=1002, name='马云', age=12, salary=9876.12}
Employee{id=1004, name='雷军', age=26, salary=7657.37}
Employee{id=1007, name='任正非', age=26, salary=4333.32}
Employee{id=1003, name='刘强东', age=33, salary=3000.82}
Employee{id=1001, name='马化腾', age=34, salary=6000.38}
Employee{id=1008, name='扎克伯格', age=35, salary=2500.32}
Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
Employee{id=1005, name='李彦宏', age=65, salary=5555.32}
*/
终端操作会从流的流水线生成结果,方法返回值类型就不再是Stream了可以是任何不是流的值(如List,Integer,void等)
方法 | 描述 |
---|---|
allMatch(Predicate p) | 检查是否匹配所有元素 |
anyMatch(Predicate p) | 检查是否至少匹配一个元素 |
noneMatch(Predicate p) | 检查是否没有匹配所有元素 |
findFirst() | 返回第一个元素 |
findAny() | 返回当前流中的任意元素,顺序流会取第一个元素 |
count() | 返回流中元素总数 |
max(Comparator c) | 返回流中最大值 |
min(Comparator c) | 返回流中最小值 |
forEach(Consumer c) | 内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。 相反,Stream API 使用内部迭代——它帮你把迭代做了) |
@Test
public void test21(){
List<Employee> employees = EmployeeData.getEmployees();
// 练习:是否所有的员工的工资是否都大于5000
System.out.println("是否所有的员工的工资是否都大于5000:"+employees.stream().allMatch(employee -> employee.getSalary() > 5000));
// 练习:是否存在员工年龄小于15
System.out.println("是否存在员工年龄小于15:"+employees.stream().anyMatch(employee -> employee.getAge() < 15));
// 练习:是否不存在员工姓“马”
System.out.println("是否不存在员工姓马:"+employees.stream().noneMatch(employee -> employee.getName().startsWith("马")));
// 返回流中的第一个元素
System.out.println("返回第一个元素:"+employees.stream().findFirst());
// 返回当前流中的任意元素
System.out.println("返回当前流中的任意元素"+employees.stream().findAny());
// 返回流中元素的总个数(返回总个数前可以先过滤)
System.out.println("返回元素总数:"+employees.stream().count());
// 返回流中最高工资
System.out.println("返回最高工资:"+employees.stream().map(Employee::getSalary).max(Double::compare));
// 返回最低工资的员工
System.out.println("返回最高工资:"+employees.stream().min(e1,e2 -> Double.compare(e1.getSalary(),e2.getSalary()));
// 返回流中最小值
System.out.println("返回最小年龄:"+employees.stream().map(Employee::getAge).min(Integer::compare));
// 内部迭代
employees.stream().forEach(System.out::println);
System.out.println("-------------");
// 使用集合的遍历操作
employees.forEach(System.out::println);
}
/*
是否所有的员工的工资是否都大于5000:false
是否存在员工年龄小于15:true
是否不存在员工姓马:false
返回第一个元素:Optional[Employee{id=1001, name='马化腾', age=34, salary=6000.38}]
返回当前流中的任意元素Optional[Employee{id=1001, name='马化腾', age=34, salary=6000.38}]
返回元素总数:8
返回最高工资:Optional[9876.12]
返回最小年龄:Optional[12]
Employee{id=1001, name='马化腾', age=34, salary=6000.38}
Employee{id=1002, name='马云', age=12, salary=9876.12}
Employee{id=1003, name='刘强东', age=33, salary=3000.82}
Employee{id=1004, name='雷军', age=26, salary=7657.37}
Employee{id=1005, name='李彦宏', age=65, salary=5555.32}
Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
Employee{id=1007, name='任正非', age=26, salary=4333.32}
Employee{id=1008, name='扎克伯格', age=35, salary=2500.32}
-------------
Employee{id=1001, name='马化腾', age=34, salary=6000.38}
Employee{id=1002, name='马云', age=12, salary=9876.12}
Employee{id=1003, name='刘强东', age=33, salary=3000.82}
Employee{id=1004, name='雷军', age=26, salary=7657.37}
Employee{id=1005, name='李彦宏', age=65, salary=5555.32}
Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
Employee{id=1007, name='任正非', age=26, salary=4333.32}
Employee{id=1008, name='扎克伯格', age=35, salary=2500.32}
*/
map和reduce的连接通常称为map-reduce模式,因Google用它来进行网络搜索而出名
方法 | 描述 |
---|---|
reduce(T identity, BinaryOperator b) | 可以将流中元素反复结合起来返回一个值T |
reduce(BinaryOperator b) | 可以将流中元素反复结合起来返回一个值Optional |
@Test
public void test22() {
List<Integer> nums = Arrays.asList(13, 32, 23, 31, 94, 20, 77, 21, 17);
List<Employee> employees = EmployeeData.getEmployees();
// 练习1:计算1-10的自然数的和(0是初始值)
System.out.println(nums.stream().reduce(0, Integer::sum));
// 练习2:手动计算公司所有员工工资总和
System.out.println(employees.stream().map(Employee::getSalary).reduce((o1, o2) -> o1 + o2));
// 调用Integer的sum方法计算年龄总和
System.out.println(employees.stream().map(Employee::getAge).reduce(Integer::sum));
// 计算公司所有员工工资综合
Stream<Double> salaryStream = employ.stream.map(Employee::getSalary);
Optional<Double> sumMoney = salaryStream.reduce(Double::sum);
System.out.println(sumMoney);
}
/*
328
Optional[48424.08]
Optional[273]
Optional[48424.08]
*/
方 法 | 描 述 |
---|---|
collect(Collector c) | 接收一个Collector接口的实现将流转换为其他形式,用于给Stream中元素做汇总的方法 |
Collector接口中方法的实现决定了如何对流执行收集的操作(如收集到List、Set、Map),可以方便地创建常见收集器实例
方法 | 返回类型 | 作用 |
---|---|---|
toList | List | 把流中元素收集到List |
List emps= list.stream().collect(Collectors.toList()); | ||
toSet | List | 把流中元素收集到List |
Set emps= list.stream().collect(Collectors.toSet()); | ||
toCollection | Collection | 把流中元素收集到创建的集合 |
Collection emps =list.stream().collect(Collectors.toCollection(ArrayList::new)); | ||
counting | Long | 计算流中元素的个数 |
long count = list.stream().collect(Collectors.counting()); | ||
summinglnt | Integer | 对流中元素的整数属性求和 |
int total=list.stream().collect(Collectors.summingInt(Employee::getSalary)); | ||
averagingInt | Double | 计算流中元素Integer属性的平均值 |
double avg = list.stream().collect(Collectors.averagingInt(Employee::getSalary)); | ||
summarizinglnt | IntSummaryStatistics | 收集流中Integer属性的统计值。如:平均值 |
int SummaryStatisticsiss=list.stream().collect(Collectors.summarizingInt(Employee::getSalary)); | ||
joining | String | 连接流中每个字符串 |
String str= list.stream().map(Employee::getName).collect(Collectors.joining()); | ||
maxBy | Optional | 根据比较器选择最大值 |
optionalmax=list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)); | ||
minBy | Optional | 根据比较器选择最小值 |
Optionalmin = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary)); | ||
reducing | 归约产生的类型 | 从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值 |
int total=list.stream().collect(Collectors.reducing(0, Employe::getSalar, Integer::sum)); | ||
collectingAndThen | 转换函数返回的类型 | 包裹另一个收集器,对其结果转换函数 |
int how= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size)); | ||
groupingBy | Map |
根据某属性值对流分组,属性为K,结果为V |
Map |
||
partitioningBy | Map |
根据true或false进行分区 |
Map |
@Test
public void test23() {
// 练习1:查找工资大于6000的员工,结果返回为一个List
List<Employee> employees = EmployeeData.getEmployees();
List<Employee> list = employees.stream().filter(employee -> employee.getSalary() > 6000).collect(Collectors.toList());
list.forEach(System.out::println);
System.out.println("--------------------");
// 练习2:查找年龄大于20的员工,结果返回为一个List
employees.add(new Employee(9527,"Kyle",21,9999));
employees.add(new Employee(9527,"Kyle",21,9999));
Set<Employee> set = employees.stream().filter(employee -> employee.getAge() > 20).collect(Collectors.toSet());
set.forEach(System.out::println);
}
/*
Employee{id=1001, name='马化腾', age=34, salary=6000.38}
Employee{id=1002, name='马云', age=12, salary=9876.12}
Employee{id=1004, name='雷军', age=26, salary=7657.37}
Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
--------------------
Employee{id=1001, name='马化腾', age=34, salary=6000.38}
Employee{id=1007, name='任正非', age=26, salary=4333.32}
Employee{id=1008, name='扎克伯格', age=35, salary=2500.32}
Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
Employee{id=1005, name='李彦宏', age=65, salary=5555.32}
Employee{id=1003, name='刘强东', age=33, salary=3000.82}
Employee{id=9527, name='Kyle', age=21, salary=9999.0}
Employee{id=1004, name='雷军', age=26, salary=7657.37}
*/
空指针异常是导致Java应用程序失败的最常见原因,以前为了解决空指针异常Google公司著名的Guava项目引入了Optional类(通过检查空值的方式来防止代码污染)
受到Google Guava的启发Java 8也引入了java.util.Optional类,Optional类是一个可以为null的容器对象
创建Optional类对象的方法
方法名 | 功能 |
---|---|
Optional.of(T t) | 创建一个Optional实例,t必须非空 |
Optional.empty() | 创建一个空的Optional实例 |
Optional.ofNullable(T t) | t可以为null |
准备实体类Boy和Girl
public class Boy {
private Girl girl;
public Boy() {
}
public Boy(Girl girl) {
this.girl = girl;
}
@Override
public String toString() {
return "Boy{" +
"girl=" + girl +
'}';
}
}
public class Girl {
private String name;
public Girl() {
}
public Girl(String name) {
this.name = name;
}
@Override
public String toString() {
return "Girl{" +
"name='" + name + '\'' +
'}';
}
}
@Test
public void test(){
Girl girl = new Girl();
// 如果girl等于null会报空指针异常
Optional<Girl> optionalGirl = Optional.of(girl);
}
@Test
public void test25(){
Girl girl = new Girl();
girl = null;
// girl可以为null
Optional<Girl> optionalGirl = Optional.ofNullable(girl);
// girl为null输出Optional.empty,girl不为null输出Optional[Girl{name="null"}]
System.out.println(optionalGirl);
// girl为null输出Girl{name="赵丽颖"},girl不为null输出Girl{name="null"}
Girl girl1 = optionalGirl.orElse(new Girl("赵丽颖"));
System.out.println(girl1);
}
判断Optional容器中是否包含对象
方法名 | 功能 |
---|---|
boolean isPresent() | 判断是否包含对象 |
void ifPresent(Consumer super T> consumer) | 如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它 |
获取Optional容器的对象
方法名 | 功能 |
---|---|
T get() | 如果调用对象包含值返回该值,否则抛异常 |
T orElse(T other) | 如果有值(内部封装的t非空)则将其返回,否则返回指定的other对象 |
T orElseGet(Supplier extends T> other) | 如果有值则将其返回,否则返回由Supplier接口实现提供的对象 |
T orElseThrow(Supplier extends X> exceptionSupplier) | 如果有值则将其返回,否则抛出由Supplier接口实现提供的异常 |
@Test
public void test26(){
Boy boy = new Boy();
boy = null;
// 此会出现空指针异常
String girlName = getGirlName(boy);
System.out.println(girlName);
}
// 获取女孩的名字,容易出现空指针异常
private String getGirlName(Boy boy) {
return boy.getGirl().getName();
}
@Test
public void test27(){
Boy boy = new Boy();
boy = null;
String girlName = getGirlName1(boy);
System.out.println(girlName);
}
// 优化以后的getGirlName():
public String getGirlName1(Boy boy){
if(boy != null){
Girl girl = boy.getGirl();
if(girl != null){
return girl.getName();
}
}
return null;
}
使用Optional类
@Test
public void test28(){
// 樱岛麻衣
Boy boy = null;
// 喜多川海梦
boy = new Boy();
// Lucy
boy = new Boy(new Girl("Lucy"));
String girlName = getGirlName2(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();
}