了解Java8新特性,看这个就够了!

了解Java8新特性,看这个就够了!

并行流与串行流

并行流:就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。相比较串行的流,并行的流可以很大程度上提高程序的执行效率。

Java8中将并行进行了优化,使我们可以很容易的对数据进行并行操作。Stream API可以声明性的通过parallel()与sequential()在并行与顺序流之间进行切换。

Lambda表达式

Lambda是一个匿名函数,可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。

从匿名类到Lambda的转换案例1:

@Test
 public void testLambda02() {
     // 常用方式
     Runnable runnable = new Runnable() {
         @Override
         public void run() {
             System.out.println("This is 【Just For Joy】.");
        }
    };
     runnable.run();
     /************Lambda表达式*************/
     Runnable runnable1 = () -> {
         System.out.println("This is Lambda Expression.");
    };
     runnable1.run();
 }

案例2:

@Test
 public void testLambda04() {
     //原来使用匿名内部类作为参数传递
     TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {
         @Override
         public int compare(String o1, String o2) {
             return Integer.compare(o1.length(), o2.length());
        }
    });
     /************Lambda表达式*************/
     TreeSet<String> ts2 = new TreeSet<>((o1, o2) -> Integer.compare(o1.length(), o2.length()));
 }

Lambada表达式语法:

Lambda表达式:在Java8语言中引入的一种新的语法元素和操作符。这个操作符为“->”,该操作符被称为Lambda操作符或者箭头操作符。它将Lambda分为两个部分:

  • 左侧:指定了Lambda表达式需要的参数列表;

  • 右侧:指定了Lambda体,是抽象方法的实现逻辑,也即Lambda表达式要执行的功能。

格式一:无参,无返回值

Runnable runnable1 = () -> {
             System.out.println("This is Lambda Expression.");
        };

格式二:Lambda需要一个参数,但是没有返回值

Consumer<String> consumer1 = (String s) -> {
             System.out.println(s);
        };

格式三:数据类型可以省略,因为可由编译器推断器推断得出,成为“类型推断“

 Consumer<String> consumer1 = (s) -> {
             System.out.println(s);
        };

格式四:Lambda若只需要一个参数时,参数的小括号可以省略

 Consumer<String> consumer1 = s -> {
             System.out.println(s);
        };

格式五:Lambda需要两个或以上的参数,多条执行语句,并且可以有返回值

 Comparator<Integer> com = (x,y) -> {
             System.out.println("实现函数式接口方法!");
             return Integer.compare(x,y);
        };

格式六:当Lambda体只有一条语句时,return与大括号若有,都可以省略

 Comparator<Integer> comparator1 = (o1, o2) -> Integer.compare(o1, o2);

类型推断:上述Lambda表达式中的参数类型都是由编译器推断得出的。Lambda表达式无需指定类型,程序依然可以编译,这是因为javac根据程序的上下文,在后台推断出了参数的类型。Lambda表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的“类型推断”。

在这里插入图片描述

函数式(Functional)接口

  • 只包含一个抽象方法的接口,成为函数式接口。
  • 可以通过Lambda表达式来创建该接口的对象。(若Lambda表达式抛出一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明)。
  • 可以在一个接口上使用@FunctionalInterface注解,这样做可以检查他是否是一个函数式接口。同时javadoc也会包含一条声明,说明这个接口是一个函数式接口。
  • 在java.util.function包下定义了Java8的丰富的函数式接口

Java从诞生日起就是一直倡导“一切皆对象”,在Java里面面向对象(OOP)编程是一切。但是随着Python、Scala等语言的兴起和新技术的挑战,Java不得不做出调整以便支持更加广泛的技术要求,也即java不但可以支持OOP还可以支持OOF(面向函数编程)。

在函数式编程语言当中,函数被当做一等公民对待。在将函数作为一等公民的编程语言当中,Lambda表达式的类型是函数。但是在Java8中,有所不同。在Java8中,Lambda表达式是对象,而不是函数,他们必须依附于一类特别的对象类型——函数式接口。

简单的说,在Java8中,Lambda表达式就是一个函数式接口的实例。这就是Lambda表达式和函数是接口的关系。也就是说,只要一个对象是函数式接口的实例,那么该对象就可以使用Lambda表达式来表示。

所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。
在这里插入图片描述

自定义函数式接口:

@FunctionalInterface
 public interface MyInterface {
     /**
      * 求和
      *
      * @param a
      * @param b
      * @return
      */
     Integer add(Integer a, Integer b);
 }

函数式接口中使用泛型:

public interface MyInterface02<T> {
     /**
      * 使用泛型
      *
      * @param t
      * @return
      */
     T getValue(T t);
 }

作为参数传递Lambda表达式:

 public String toUpperString(MyInterface02<String> mf, String str) {
     return mf.getValue(str);
 }
 
 @Test
 public void testLambda05() {
     String str = toUpperString(s -> s.toUpperCase(), "lixiaochangx");
     System.out.println(str);
 }

Java内置四大核心函数式接口

函数式接口参数类型返回类型用途
Consumer消费型接口Tvoid对类型为T的对象应用操作,包含方法:void accept(T t)
Supplier供给型接口T返回类型为T的对象,包含方法:T get()
Function<T,R>函数型接口TR对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t)
Predicate断言型接口TBoolean确定类型为T的对象是否满足某约束,并返回Boolean值,包含方法:boolean test(T t)

其他接口:
在这里插入图片描述

方法引用与构造器引用

方法引用(Method References)

  • 当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用。
  • 当方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。
  • 要求:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法参数列表和返回值类型保持一致!
  • 格式:使用操作符“::”将类(或对象)与方法名分割开来。

如以下三种情况:

  • 对象::实例方法名
  • 类::静态方法名
  • 类::实例方法名
Consumer<String> con = x -> System.out.println(x);
 Comparator<Integer> comparator = (o1, o2) -> Integer.compare(o1, o2);
 BiPredicate<String, String> predicate = (s1, s2) -> s1.equals(s2);
 // 等同于
 Consumer<String> con1 = System.out::println;
 Comparator<Integer> comparator1 = Integer::compareTo;
 BiPredicate<String,String> predicate1 = String::equals;

注意:当函数式接口方法的第一个参数时需要引用方法的调用者,并且第二个参数时需要引用方法的参数(或无参数)时:ClassName::methodName。

构造器引用

格式:ClassName::new

与函数式接口相结合,自动与函数式接口中方法兼容。

可以把构造器引用赋值给定义的方法,要求构造器参数列表与接口中抽象方法的参数列表一致!切方法的返回值即为构造器对应类的对象。

/**
  * 构造器引用
  * Supplier中的T get()
  * Employee的空参构造器:Employee()
  */
 @Test
 public void test01() {
     Supplier<Employee> employeeSupplier = new Supplier<Employee>() {
         @Override
         public Employee get() {
             return new Employee();
        }
    };
 
     System.out.println("***************************");
     Supplier<Employee> supplier = () -> new Employee();
     System.out.println("supplier:" + supplier.get());
 
     System.out.println("***************************");
     Supplier<Employee> supplier1 = Employee::new;
     System.out.println("supplier:" + supplier1.get());
 }

Stream API

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

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

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

  • 实际开发中,项目中多数数据源都来自MySQL,Oracle等。但现在数据源可以更多了,有MongoDB,Redis等,而这些NoSQL的数据就需要Java层面去处理。

  • Stream和Collection集合的区别:Collection是一种静态的内存数据结构,而Stream是有关计算的。前者是主要面向内存,存储在内存中,后者主要是面向CPU,通过CPU实现计算。

Stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。

集合讲的是数据,Stream讲的是计算!

注意:

  • Stream自己不会存储元素;
  • Stream不会改变源对象。相反,它们会返回一个持有结果的新Stream;
  • Stream操作是延迟执行的。这意味着它们会等到需要结果的时候才执行。

Stream的创建步骤:

  • 创建Stream
    一个数据源(如集合、数组),获取一个流。
  • 中间操作
    一个中间操作链,对数据源的数据进行处理。
  • 终止操作
    一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用。
    在这里插入图片描述

创建Stream方式一:通过集合

java8中的Collection接口被扩展,提供两个获取流的方法:

default Stream stream():返回一个顺序流

default Stream parallel():返回一个并行流

/**
  * 创建Stream方式一:通过集合
  */
 @Test
 public void test01() {
     List<Employee> list = Employee.getEmployees();
 
     //default Stream<E> stream() 返回一个顺序流
     Stream<Employee> stream = list.stream();
     //default Stream<E> parallelStream 返回一个并行流
     Stream<Employee> parallelStream = list.parallelStream();
 }

创建Stream方式二:通过数组

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

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

重载形式,能够处理对应基本类型的数组:

  • public static IntStream stream(int[] array)
  • public static LongStream stream(int[] array)
  • public static DoubleStream stream(int[] array)
/**
  * 创建Stream方式二:通过数组
  */
 @Test
 public void test02() {
     int[] arr = {1, 2, 3, 4, 5};
     // 调用Arrays类的static <T> Stream<T> stream(T[] array):返回一个流
     IntStream stream = Arrays.stream(arr);
 
     Employee jerry = new Employee(1001, "Jerry");
     Employee tom = new Employee(1001, "Tom");
     Employee[] employees = {jerry, tom};
     Stream<Employee> stream1 = Arrays.stream(employees);
 }

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

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

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


@Test
 public void test03() {
 Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
 }

创建Stream方式四:创建无限流

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

  • 迭代
    public static Stream iterate(final T seed, final UnaryOperator f)
  • 生成
    public static Stream generate(Supplier s)
@Test
 public void test04() {
     // 迭代
     // 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);
 }

Stream的中间操作

多个中间操作可以连接起来形成一个流水线,除非流水线上出发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,成为“惰性求值”。

筛选与切片
方法描述
filter(Predicate p)接收Lambda,从流中排除某些元素
distinct()筛选,通过流所生成元素的hashCode和equals去除重复元素
limit(long maxSize)截断流,使其元素不超过给定数量
skip(long n)跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补
@Test
 public void test01() {
     List<Employee> list = Employee.getEmployees();
     // filter(Predicate p)——接受 Lambda,从流中排除某些元素
     Stream<Employee> stream = list.stream();
     // 练习:查询员工表中薪资大于7000的员工信息
     stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println);
 
     System.out.println();
     // limit(n)——截断流,使其元素不超过给定数量
     list.stream().limit(3).forEach(System.out::println);
     System.out.println();
 
     // 跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补
     list.stream().skip(3).forEach(System.out::println);
 
     System.out.println();
     // distinct() ——筛选,通过流所生成元素的hashCode() 和 equals() 去除重复元素
     list.add(new Employee(1010, "刘强东", 40, 9999));
     list.add(new Employee(1010, "刘强东", 40, 9999));
     list.add(new Employee(1010, "刘强东", 40, 9999));
     list.add(new Employee(1010, "刘强东", 40, 9999));
     list.add(new Employee(1010, "刘强东", 40, 9999));
 
     // System.out.println(list);
     list.stream().distinct().forEach(System.out::println);
 }
映射
方法描述
map(Function f)接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
mapToDouble(ToDoubleFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的DoubleStream。
mapToInt(ToIntFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的IntStream。
mapToLong(ToLongFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的LongStream。
flatMap(Function f)接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

 @Test
 public void test02() {
     // map(Function f)——接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素。
     List<String> list = Arrays.asList("aa", "bb", "cc");
     list.stream().map(s -> s.toUpperCase()).forEach(System.out::println);
 
     // 练习1 获取员工姓名长度大于3的员工的姓名
     List<Employee> employees = Employee.getEmployees();
     Stream<String> nameStream = employees.stream().map(Employee::getName);
     nameStream.filter(name -> name.length() > 3).forEach(System.out::println);
     System.out.println();
 
     // 练习2
     Stream<Stream<Character>> streamStream = list.stream().map(StreamApi02Test::fromStringToStream);
     streamStream.forEach(s -> s.forEach(System.out::println));
     System.out.println();
 
     // flatMap(Function f)——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
     Stream<Character> characterStream = list.stream().flatMap(StreamApi02Test::fromStringToStream);
     characterStream.forEach(System.out::println);
 }
排序
方法描述
sorted()产生一个流,其中按自然顺序排序
sorted(Comparator com)产生一个流,其中按比较器顺序排序
@Test
 public void test04() {
     // sorted ——自然排序
     List<Integer> list = Arrays.asList(12, 83, 54, 67, 0, -90, 7);
     list.stream().sorted().forEach(System.out::println);
     // 抛异常,原因:Employee没有实现Comparable接口
     List<Employee> employees = Employee.getEmployees();
     employees.stream().sorted().forEach(System.out::println);
 
     // sorted(Comparator com)——定制排序
     /*List<Employee> employees = Employee.getEmployees();
         employees.stream().sorted( (e1,e2) -> {
             int ageValue = Integer.compare(e1.getAge(),e2.getAge());
             if(ageValue != 0){
                 return ageValue;
             }else{
                 return -Double.compare(e1.getSalary(),e2.getSalary());
             }
         }).forEach(System.out::println);*/
 }

Stream的终止操作
匹配与查找
方法描述
count()返回流中元素总数
max(Comparator c)返回流中最大值
min(Comparator c)返回流中最小值
forEach(Consumer c)内部迭代(使用Collection接口需要用户去做迭代,成为外部迭代。相反,Stream API使用内部迭代——他帮你把迭代做了)
@Test
 public void test01() {
     List<Employee> employees = Employee.getEmployees();
 
     // allMatch(Predicate p)——检查是否匹配所有元素
     // 练习 是否所有员工的年龄都大于18
     boolean allMatch = employees.stream().allMatch(employee -> employee.getAge() > 18);
     System.out.println(allMatch);
 
     // anyMatch(Predicate p)——检查是否至少匹配一个元素。
     // 练习:是否存在员工的工资大于 10000
     boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000);
     System.out.println(anyMatch);
 
     // noneMatch(Predicate p)——检查是否没有匹配的元素。
     // 练习:是否存在员工姓“马”
     boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("马"));
     System.out.println(noneMatch);
 
     // findFirst——返回第一个元素
     Optional<Employee> employee = employees.stream().findFirst();
     System.out.println(employee);
 
     // findAny——返回当前流中的任意元素
     Optional<Employee> employee1 = employees.parallelStream().findAny();
     System.out.println(employee1);
 }
 
 @Test
 public void test2() {
     List<Employee> employees = Employee.getEmployees();
 
     // count——返回流中元素的总个数
     long count = employees.stream().filter(e -> e.getSalary() > 5000).count();
     System.out.println(count);
 
     // max(Comparator c)——返回流中最大值
     // 练习:返回最高的工资:
     Stream<Double> salaryStream = employees.stream().map(e -> e.getSalary());
     Optional<Double> maxSalary = salaryStream.max(Double::compare);
     System.out.println(maxSalary);
 
     // min(Comparator c)——返回流中最小值
     // 练习:返回最低工资的员工
     Optional<Employee> employee = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
     System.out.println(employee);
     System.out.println();
 
     // forEach(Consumer c)——内部迭代
     employees.stream().forEach(System.out::println);
 
     System.out.println();
     // 使用集合的遍历操作
     employees.forEach(System.out::println);
 }

归约
方法描述
reduce(T iden,BinaryOperator b)可以将流中元素反复结合起来,得到一个值,返回T
reduce(BinaryOoperator b)可以将流中元素反复结合起来,得到一个值,返回Optional

备注:map和reduce的连接通常成为map-reduce模式,因Google用它来进行网络搜索而出名。

@Test
 public void test3() {
     // reduce(T identity, BinaryOperator)——可以将流中元素反复结合起来,得到一个值。返回 T
     // 练习1:计算1-10的自然数的和
     List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
     Integer sum = list.stream().reduce(0, Integer::sum);
     System.out.println(sum);
 
     // reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值。返回 Optional<T>
     // 练习2:计算公司所有员工工资的总和
     // Optional<Double> sumMoney = salaryStream.reduce(Double::sum);
     List<Employee> employees = Employee.getEmployees();
     Stream<Double> salaryStream = employees.stream().map(Employee::getSalary);
     // Optional<Double> sumMoney = salaryStream.reduce(Double::sum);
     Optional<Double> sumMoney = salaryStream.reduce((d1, d2) -> d1 + d2);
     System.out.println(sumMoney);
 }
收集
方法描述
collect(Collector c)将流转换为其他形式。接收一个Collector接口的实现,用于给Stream元素做汇总的方法
@Test
 public void test4() {
     // collect(Collector c)——将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
     // 练习1:查找工资大于6000的员工,结果返回为一个List或Set
     List<Employee> employees = Employee.getEmployees();
     List<Employee> employeeList = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
 
     employeeList.forEach(System.out::println);
     System.out.println();
     Set<Employee> employeeSet = employeeList.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet());
     employeeList.forEach(System.out::println);
 }

Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到List、Set、Map)。
另外,Collectors实现类提供了很多静态方法,可以方便的创建常见收集器实例,具体方法如下。
在这里插入图片描述

在这里插入图片描述

Optional类

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

Optional类(java.util.Optional)是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。原来用null表示一个值不存在,现在Optional可以更好的表达这个概念。并且可以避免空指针异常。

Optional类的javadoc描述如下:这是一个可以为null的容器对象。如果值存在则isPresent方法会返回true,调用get方法会返回该对象。

Optional提供很多有用的方法,这样我们就不用显示进行空值检测。

创建Optional类对象的方法

  • Optional.of(T t):创建一个Optional实例,t必须非空
  • Optional.empty():创建一个空的Optional实例
  • Optional.ofNullable(T t):t可以为null

判断Optional容器中是否包含对象:

  • boolean isPresent():判断是否包含对象。
  • void ifPresent(Consumer<? super T> consumer):如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它。

获取Optional容器的对象:

  • T get():如果调用对象包含值,返回该值,否则抛异常。
  • T orElse(T other):如果有值则将其返回,否则返回指定的other对象。
  • T orElseGet(Supplier<? extends T> other):如果有值则将其返回,否则返回由Supplier接口实现提供的对象。
  • T orElseThrow(Supplier<? extends X exceptionSupplier):如果有值,则将其返回,否则抛出由Supplier接口实现提供的异常。
 public class OptionalTest {
 
     /**
      * Optional.of(T t) : 创建一个 Optional 实例,t必须非空;
      * Optional.empty() : 创建一个空的 Optional 实例
      * Optional.ofNullable(T t):t可以为null
      */
 
     @Test
     public void test01() {
         Girl girl = new Girl();
         // girl = null;
         //of(T t):保证t是非空的
         Optional<Girl> optionalGirl = Optional.of(girl);
    }
 
     @Test
     public void test02() {
         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);
 
    }
 
     public String getGirlName(Boy boy) {
         return boy.getGirl().getName();
    }
 
     @Test
     public void test3() {
         Boy boy = new Boy();
         boy = null;
         String girlName = getGirlName(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;
    }
 
     @Test
     public void test4() {
         Boy boy = new Boy();
         boy = null;
         String girlName = getGirlName1(boy);
         System.out.println(girlName);
    }
 
     /**
      * 使用Optional类的getGirlName():
      *
      * @param boy
      * @return
      */
     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);
    }
 
 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值