JDK8新特性

1 Java 8 新特性简介

Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本。Java 8 是oracle公司于2014年3月发布,可以看成是自Java 5 以来最具革命性的版本。 Java 8为Java语言、编译器、类库、开发工具与JVM带来了大量新特性。

2 Lambda表达式

2.1 简介

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

2.2 案例演示

从匿名类到 Lambda 的转换

案例一

public class LambdaDemo1 {
    public static void main(String[] args) {
//        Runnable mytask = new Runnable() {
//            @Override
//            public void run() {
//                for (int i = 1; i <= 100; i++) {
//                    System.out.println(Thread.currentThread().getName()+ " "+ i);
//                }
//            }
//        };
        Runnable mytask = () ->{
            for (int i = 1; i <= 100; i++) {
                    System.out.println(Thread.currentThread().getName()+ " "+ i);
                }
        };
        
        for (int i = 0; i < 4; i++) {
            new Thread(mytask).start();
        }
        System.out.println("main over");
    }
}

案例二

public class LambdaDemo2 {
    @FunctionalInterface
    interface A{
        void f1();
    }
    public static void show(A a) {
        a.f1();
    }

    public static void main(String[] args) {
//        A a = new A() {
//            @Override
//            public void f1() {
//                System.out.println("a f1");
//            }
//
//            @Override
//            public void f2() {
//                System.out.println("a f2");
//            }
//
//        };
//        show(a);
        A a = ()->{
            System.out.println("f1");

        };
        show(a);
    }

}

2.3 语法

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

  • 匿名内部类实现的接口一定是函数接口(只有一个抽象方法的接口)
  • 左侧: 指定了 Lambda 表达式需要的参数列表
  • 右侧: 指定了 Lambda 体, 是抽象方法的实现逻辑,也即Lambda 表达式要执行的功能。

2.4 类型推断

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

案例演示

案例一:一个参数

public class LambdaDemo3 {
    public static void show(A a){
        a.f1("hello lambda 表达式");
    }
    @FunctionalInterface
    interface A{
        void f1(String s);
    }
    /**
     * 匿名内部类
     */
    @Test
    public void test1(){
        A a = new A() {
            @Override
            public void f1(String s) {
                System.out.println(s);
            }
        };
        show(a);
    }
    /**
     * lambda表达式
     */
    @Test
    public void test2(){
//        A a = (String s)->{
//            System.out.println(s);
//        };
//        show(a);
        show((String s)->{
            System.out.println(s);
        });

    }

    /**
     * lambda表达式左边参数列表,可以根据上下文判断参数类型
     */
    @Test
    public void test3(){
        show((s)->{
            s += "abc";
            System.out.println(s);
        });
    }

}

案例二:两个参数

public class LambdaDemo4 {
    public static void show(A a){
        a.f1(10,20);
    }
    @FunctionalInterface
    interface A {
        void f1(int x,int b);
    }
    @Test
    public void test1(){
        A a = new A() {
            @Override
            public void f1(int x, int y) {
                System.out.println(x>y?x:y);
            }
        };
        show(a);

    }

    @Test
    public void test2(){
        show((x, y) -> {
            System.out.println(x>y?x:y);
        });
    }

    @Test
    public void test3() {
        List list = new ArrayList();

        list.add("555");
        list.add("zzz");
        list.add("xxx");
        list.add("yyy");
        list.add("mmm");
        list.add("nnn");
        list.add("aaa");
        list.add("aaa");
        list.add("111");
        list.add("111");
        list.add("222");
        list.add("333");
        list.add("444");

//        Comparator com = new Comparator() {
//            @Override
//            public int compare(Object o1, Object o2) {
//                String s1 = (String) o1;
//                String s2 = (String) o2;
//                return -s1.compareTo(s2);
//            }
//
//        };
//        Collections.sort(list, com); //定制排序
//
//        System.out.println(list);
        
        //定制排序器
        Comparator<String> com = (s1, s2) -> {
            return -s1.compareTo(s2);
        };
        Collections.sort(list, com); //定制排序

        System.out.println(list);
    }
}

3 函数式接口

3.1 接口的增强

  • 函数式接口中有且只有一个抽象方法
  • 函数式接口中可以有默认函数
@FunctionalInterface
public interface InterfaceTest {

    void f1();

    default void f2() {
        System.out.println("aaaa");
    }

}

3.2 什么是函数式(Functional)接口

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

3.3 如何理解函数式接口

  • Java从诞生日起就是一直倡导“一切皆对象”, 在Java里面面向对象(OOP)编程是一切。但是随着python、 scala等语言的兴起和新技术的挑战, Java不得不做出调整以便支持更加广泛的技术要求,也即java不但可以支持OOP还可以支持OOF(面向函数编程)
  • 在函数式编程语言当中,函数被当做一等公民对待。在将函数作为一等公民的编程语言中, Lambda表达式的类型是函数。但是在Java8中,有所不同。在Java8中,Lambda表达式是对象,而不是函数,它们必须依附于一类特别的对象类型——函数式接口。
  • 简单的说,在Java8中, Lambda表达式就是一个函数式接口的实例。 这就是Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口的实例,那么该对象就可以用Lambda表达式来表示。
  • 所以以前用匿名实现类表示的现在都可以用Lambda表达式来写

3.4 函数式接口举例

自定义函数式接口

@FunctionalInterface
 public interface MyInterface {
 
     void method1();
 
 //    void method2();
 }

3.5 Java内置函数式接口

四大核心函数式接口

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

其他接口

函数式接口参数类型返回类型用途
BiFunction<T, U, R>T, UR对类型为 T, U 参数应用操作, 返回 R 类型的结 果。 包含方法为: R apply(T t, U u);
UnaryOperator (Function子接口)TT对类型为 结果。 包含方法 T的对象进行一元运算 为: T apply(T t),; 并返回T类型的
BinaryOperator (BiFunction 子接口)T, TT对类型为 结果。 包含方法 T的对象进行二元运算 为: T apply(T,t1并返回 , T t2);T类型的
BiConsumer<T, U>T, Uvoid对类型为T, U 参数应用操作。 包含方法为: void accept(T t, U u)
BiPredicate<T,U>T,Uboolean包含方法为: boolean test(T t,U u)
ToIntFunction ToLongFunction ToDoubleFunctionTint long double分别计算int、 long、 double值的函数
IntFunction LongFunction DoubleFunctionRint long double参数分别为int、 long、 double 类型的函数

3.6 案例演示

案例一

public class ConsumerTest {
    @Test
    public void test1(){
        List<User> list = new ArrayList<>();
        list.add(new User(1, "mickey", 18));
        list.add(new User(2, "john", 18));
        list.add(new User(3, "doc", 18));
        list.add(new User(4, "cat", 18));
        list.add(new User(5, "tom", 18));
        list.add(new User(6, "tomcat", 18));
        list.add(new User(7, "mysql", 18));
         list.add(new User(8, "java", 18));

//        Consumer<User> c = new Consumer<User>() {
//            @Override
//            public void accept(User o) {
//                System.out.println(o);
//            }
//        };
        Consumer<User> c = o -> System.out.println(o);
        list.forEach(c);

    }
    @Test
    public void test2(){
//        Consumer<String> c = new Consumer<String>() {
//            @Override
//            public void accept(String s) {
//                System.out.println(s);
//            }
//        };
        Consumer<String> c = s -> System.out.println(s);
        Consumer<String> c2 = s -> {
            System.out.println(s.substring(6));
        };
        c.accept("hello world");
        c2.accept("hello world");
    }

    /**
     * 处理金额
     * @param c
     * @param money
     */
    public void f1(Consumer<Double> c, Double money) {
        c.accept(money);
    }
    @Test
    public void test3(){
        f1(m ->{
            DecimalFormat df = new DecimalFormat("#,###.##");
            System.out.println(df.format(m));
        },1234562345.789);
        f1(m -> {
            DecimalFormat df = new DecimalFormat("0,000.000");
            System.out.println(df.format(m));
        },1234562345.213241);
    }

    /**
     * DecimalFormat保留2位小数,每三位一个多厚(金额)
     */
    @Test
    public void test4 (){
        DecimalFormat df = new DecimalFormat("#,###.##");
        System.out.println(df.format(12345623434.789));
    }
}

案例二

public class PredicatTest {
    @Test
    public void test1(){
        List<User> list = new ArrayList<>();
        list.add(new User(1, "mickey", 22));
        list.add(new User(2, "john", 33));
        list.add(new User(3, "doc", 19));
        list.add(new User(4, "cat", 20));
        list.add(new User(5, "tom", 20));
        list.add(new User(6, "tomcat", 70));
        list.add(new User(7, "mysql", 73));
        list.add(new User(8, "java", 84));

        /**
         * 删除年龄是20的元素
         */
        list.removeIf(user -> user.getAge() == 20);
        System.out.println(list);
        System.out.println("---------");

        /**
         * 查找>=5的用户,循环
         */
        list.stream().filter(user -> {
            return user.getUid() >= 5;
        }).forEach(user -> {
            System.out.println(user);
        });
    }

    public static List<String> filterList(List<String> list, Predicate<String> p){
        List<String> result = new ArrayList<>();
        for (String s : list) {
            if (p.test(s)){
                result.add(s);
            }
        }
        return result;
    }

    @Test
    public void test2(){
        List<String> list = new ArrayList<>();
        list.add("hello abc");
        list.add("welcome abc");
        list.add("hello world");
        list.add("你好 abc");
        list.add("大家好 abc");
        list.add("hello mickey");

        filterList(list, s -> {
            return s.contains("hello");
        }).forEach(s -> System.out.println(s));

        System.out.println("--------------");

        filterList(list, s -> {
            return s.contains("abc");
        }).forEach(s -> System.out.println(s
        ));

    }

}

案例三

public class FunctionTest {
    @Test
    public void test1(){
        List<User> list = new ArrayList<>();
        list.add(new User(1, "mickey", 18));
        list.add(new User(2, "john", 18));
        list.add(new User(3, "doc", 18));
        list.add(new User(4, "cat", 18));
        list.add(new User(5, "tom", 18));
        list.add(new User(6, "tomcat", 18));
        list.add(new User(7, "mysql", 18));
        list.add(new User(8, "java", 18));

//        //得到集合中的id
//        List<Integer> ids = new ArrayList<>();
//        for (User user : list) {
//            ids.add(user.getId());
//        }

        //得到集合中的所有的id
        List<String> ids = list.stream().map(user -> user.getUid() + user.getName()).collect(Collectors.toList());
        System.out.println(ids);
    }

    public Object performUser(Function<User,Object> f,User u){
        return f.apply(u);
    }

    @Test
    public void test2(){
        User user = new User(1, "mickey", 18);

        //得到学员名字
        System.out.println(performUser(u -> {
            return u.getName();
        },user));
    }
}

案例四

public class SupplierTest {
    public static void printBean(Supplier s){
        System.out.println(s.get());
    }
    @Test
    public void test1(){
        printBean(() -> new User(1,"mickey",18));
        System.out.println("---------");
        printBean(() -> "hello stream");
    }
}

案例五

public class BinaryOperatorTest {
    @Test
    public void test1(){
        List<User> list = new ArrayList<>();
        list.add(new User(1, "mickey", 18));
        list.add(new User(2, "john", 18));
        list.add(new User(3, "doc", 18));
        list.add(new User(4, "cat", 18));
        list.add(new User(5, "tom", 18));
        list.add(new User(6, "tomcat", 18));
        list.add(new User(7, "mysql", 18));
        list.add(new User(8, "java", 18));

        //得到所有的id,然后相加得到结果 -> List<Integer> ids =
        Optional<Integer> optionalInteger = list.stream().map(user -> user.getUid()).reduce((d1,d2) -> d1 + d2);
        System.out.println(optionalInteger.get());
    }
}

4 方法引用

方法引用是Lambda表达式的一个语法糖:

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

4.1 语法

    /**
     * lambda表达式的语法糖
     */
    @Test
    public void test2() {
        printBean(User :: new);
    }

4.2 案例演示

案例一

    @Test
    public void test1() {
        List<User> list = new ArrayList<>();
        list.add(new User(1, "mickey", 18));
        list.add(new User(2, "john", 18));
        list.add(new User(3, "doc", 18));
        list.add(new User(4, "cat", 18));
        list.add(new User(5, "tom", 18));
        list.add(new User(6, "tomcat", 18));
        list.add(new User(7, "mysql", 18));
        list.add(new User(8, "java", 18));

        //匿名内部类
        list.forEach(new Consumer<User>() {
            @Override
            public void accept(User user) {
                System.out.println(user);
            }
        });

        System.out.println("-------------");

        /**
         * stream中使用Consumer
         */
        list.forEach(o -> System.out.println(o));
        System.out.println("---------------");

        //方法引用
        list.forEach(System.out :: println);
    }

案例二

    @Test
    public void test5() {
        //匿名内部类
        int i1 = f1(new BiFunction<Integer, Integer, Integer>() {
            @Override
            public Integer apply(Integer d1, Integer d2) {
                return Integer.max(d1, d2);
            }
        }, 12, 15);

        System.out.println("--------");
        //lambda表达式
        int i2 = f1((d1, d2) -> Integer.max(d1, d2), 12, 15);

        //方法引用-lambda表达式的语法糖
        int i3 = f1(Integer::max, 12, 15);

        System.out.println(i1 + " " + i2 + " " + i3);

    }

5 Stream API

5.1 Stream简介

  • Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API。
  • Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充,因为Stream API可以极大提供Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
  • Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简言之, Stream API 提供了一种高效且易于使用的处理数据的方式
  • Stream 和 Collection 集合的区别: Collection 是一种静态的内存数据结构,而 Stream 是有关计算的。 前者是主要面向内存,存储在内存中,后者主要是面向 CPU,通过 CPU 实现计算。
  • Stream到底是什么呢?是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据, Stream讲的是计算!”
    • Stream 自己不会存储元素。
    • Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
    • Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行

5.2 Stream使用

  • 创建 Stream:通过一个数据源(如:集合、数组),获取一个流
  • 中间操作:一个中间操作链,对数据源的数据进行处理
  • 终止操作(终端操作):一旦执行终止操作, 就执行中间操作链,并产生结果。之后,不会再被使用

5.3 创建 Stream方式

  • Collection 接口,提供了两个获取流的方法:
    • default Stream stream() : 返回一个顺序流
    • default Stream parallelStream() : 返回一个并行流
  • Arrays 的静态方法 stream() 可以获取数组流
    • static Stream stream(T[] array): 返回一个流
  • 通过Stream的of() 获取
    • public static Stream of(T… values) : 返回一个流

5.4 Stream 的中间操作

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

  • 筛选与切片
方 法描 述
filter(Predicate p)接收 Lambda , 从流中排除某些元素
distinct()筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
limit(long maxSize)截断流,使其元素不超过给定数量
skip(long n)跳过元素,返回一个扔掉了前 个空流。与 limit(n) 互补 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
  • 映 射
方法描述
map(Function f)接收一个函数作为参数,该函数会被应用到每个元 素上,并将其映射成一个新的元素。
mapToDouble(ToDoubleFunction f)接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 DoubleStream。
mapToInt(ToIntFunction f)接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 IntStream。
mapToLong(ToLongFunction f)接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 LongStream。
flatMap(Function f)接收一个函数作为参数,将流中的每个值都换成另 一个流,然后把所有流连接成一个流
  • 排 序
方法描述
sorted()产生一个新流,其中按自然顺序排序
sorted(Comparator com)产生一个新流,其中按比较器顺序排序

5.5 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 使用内部迭 代——它帮你把迭代做了)
  • 归约
方法描述
reduce(T iden, BinaryOperator b)可以将流中元素反复结合起来,得到一 个值。返回 T
reduce(BinaryOperator b)可以将流中元素反复结合起来,得到一 个值。返回 Optional
  • 收集
方 法描 述
collect(Collector c)将流转换为其他形式。接收一个 Collector 接口的实现,用于给Stream中元素做汇总 的方法

Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、 Set、Map)。另外, Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:

5.6 案例演示

public class StreamTest1 {


    /**
     * 收集:stream的最终操作
     *   - forEach
     *   - allMatch
     *   - collect
     */
    @Test
    public void test7() {
        List<User> list = new ArrayList<>();
        list.add(new User(1, "mickey", 22));
        list.add(new User(2, "johna", 33));
        list.add(new User(3, "doca", 19));
        list.add(new User(4, "cat", 20));
        list.add(new User(5, "toma", 20));
        list.add(new User(6, "tomcat", 70));
        list.add(new User(7, "mysqla", 73));
        list.add(new User(8, "java", 84));

        //得到年龄30以上的用户的id组成的set集合
        Set<Integer> ids = new HashSet<>();
        for (User user : list) {
            if (user.getAge() > 30) {
                ids.add(user.getId());
            }
        }
        System.out.println(ids);
        System.out.println("------------");
        //Stream写法
        Set ids2 = list.stream().filter(u -> u.getAge() > 30).map(u -> u.getId()).collect(Collectors.toSet());
        System.out.println(ids);

    }



    /**
     * allMatch
     */
    @Test
    public void test6() {
        List<User> list = new ArrayList<>();
        list.add(new User(1, "mickey", 22));
        list.add(new User(2, "johna", 33));
        list.add(new User(3, "doca", 19));
        list.add(new User(4, "cat", 20));
        list.add(new User(5, "toma", 20));
        list.add(new User(6, "tomcat", 70));
        list.add(new User(7, "mysqla", 73));
        list.add(new User(8, "java", 84));

        System.out.println(list.stream().allMatch(u -> u.getName().contains("a")));

    }


    @Test
    public void test3() {
        //以()代表流,[]为数组,""为字符串
        List<String> list = Arrays.asList("a-b-c", "d-e-f");

        //map中的第一个t是"a-b-c",返回的是[a,b,c]一个数组
        //map以后流中的元素为([a,b,c],[e,d,f])两个元素,每个元素是数组
        //map: ("a-b-c", "d-e-f")---->([a,b,c],[e,d,f]) ,size=2
        list.stream().map(t -> t.split("-")).forEach(e -> System.out.println(Arrays.toString(e)));

        System.out.println("-----------");

        //flatMap第一个t是"a-b-c",返回的是(a,b,c)的一个子流,最后聚合所有子流合并为一个流
        //flatMap: ("a-b-c", "d-e-f")---->((a,b,c),(d,e,f))-->(a,b,c,d,e,f) ,size=6
        list.stream().flatMap(t -> Stream.of(t.split("-"))).forEach(System.out::println);
    }


    @Test
    public void test2() {
        List<User> list = new ArrayList<>();
        list.add(new User(1, "mickey", 22));
        list.add(new User(2, "john", 33));
        list.add(new User(3, "doc", 19));
        list.add(new User(4, "cat", 20));
        list.add(new User(5, "tom", 20));
        list.add(new User(6, "tomcat", 70));
        list.add(new User(7, "mysql", 73));
        list.add(new User(8, "java", 84));

        //对age排序
        //匿名内部类
        list.stream().sorted(new Comparator<User>() {
            @Override
            public int compare(User o1, User o2) {
                return Integer.compare(o1.getAge(), o2.getAge());
            }
        }).forEach(System.out :: println);

        System.out.println("----------------");
        //对age排序
        //lambda
        list.stream().sorted((o1, o2) -> Integer.compare(o1.getAge(), o2.getAge())).forEach(System.out :: println);
    }


    @Test
    public void test1() {
        List<User> list = new ArrayList<>();
        list.add(new User(1, "mickey", 22));
        list.add(new User(2, "john", 33));
        list.add(new User(3, "doc", 19));
        list.add(new User(4, "cat", 20));
        list.add(new User(5, "tom", 20));
        list.add(new User(6, "tomcat", 70));
        list.add(new User(7, "mysql", 73));
        list.add(new User(8, "java", 84));

        //得到上面集合id >=4的用户的age,得到age的总和
//        list.stream().filter(u -> u.getId() >= 4).map(u -> u.getAge()).forEach(System.out :: println);
        System.out.println(list.stream().filter(u -> u.getId() >= 4).map(u -> u.getAge()).reduce((d1, d2) -> d1 + d2).get());


        List<Integer> ages = list.stream().filter(u -> u.getId() <= 6).map(u -> {
            return u.getAge();
        }).collect(Collectors.toList());
        System.out.println(ages);
        //查找id<=6的用户,所有的age相加得到结果
        Optional<Integer> optionalInteger = list.stream().filter(u -> u.getId() <= 6).map(u -> {
            return u.getAge();
        }).collect(Collectors.toList()).stream().reduce((d1, d2) -> d1 + d2);
        System.out.println(optionalInteger.get());
    }
}

6 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必须非空;
    • 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 Girl {
 
     private String name;
 
     @Override
     public String toString() {
         return "Girl{" +
                 "name='" + name + '\'' +
                 '}';
     }
 
     public String getName() {
         return name;
     }
 
     public void setName(String name) {
         this.name = name;
     }
 
     public Girl() {
 
     }
 
     public Girl(String name) {
 
         this.name = name;
     }
 }
public class Boy {
     private Girl girl;
 
     @Override
     public String toString() {
         return "Boy{" +
                 "girl=" + girl +
                 '}';
     }
 
     public Girl getGirl() {
         return girl;
     }
 
     public void setGirl(Girl girl) {
         this.girl = girl;
     }
 
     public Boy() {
 
     }
 
     public Boy(Girl girl) {
 
         this.girl = girl;
     }
 }

测试类

public class OptionalTest {
 
 /*
 Optional.of(T t) : 创建一个 Optional 实例,t必须非空;
 Optional.empty() : 创建一个空的 Optional 实例
 Optional.ofNullable(T t):t可以为null
 
  */
     @Test
     public void test1(){
         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);
 
     }
 
 
     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():
     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);
 
     }
 
 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值