java8新特新学习笔记

jdk8新特性内容概述

  1. lambad表达式
  2. 函数式接口
  3. 方法引用与构造器引用
  4. StreamAPI
  5. optinal类

jdk8新特性简介

jdk8(又称为java8)是java开发的一个主要版本。jdk8是oracle公司2014年3月份发布的,可以看成是jdk5发布以来最具有革命性的一代。jdk8为java语言。编译器、开发工具与jvm带来了大量新特性。

  • 速度更快
  • 代码更少(增加了新的语法:lambda表达式)
  • 强大的streamAPI
  • 便于并行
  • 最大化减少空指针异常:optional
  • Nashorn引擎,支持在jvm上运行js应用。

jvm上运行js

function f(){
	return 1;
}
print(f() +1);
jjs js测试.js(文件名) 

Lambda表达式

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

 @Test
    public void test(){
        Runnable r1 = new Runnable(){

            @Override
            public void run() {
                System.out.println("需求做不完");
            }
        };
        r1.run();
        System.out.println("*******************************");
        Runnable r2 = () -> System.out.println("hello"); 
        r2.run();
    }
@Test
    public void test2(){
        Comparator<Integer> comparator1 = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1, o2);
            }
        };

        int compare1 = comparator1.compare(55, 44);
        System.out.println(compare1);
        System.out.println("*********************************");;
        //Lambda表达式写法
        Comparator<Integer> comparator2 = (o1,o2)->{
            return Integer.compare(o1,o2);
        };
        System.out.println(comparator2.compare(88,99));
        System.out.println("***********************");

        //方法引用写法
        Comparator<Integer> comparator3 = Integer::compare;
        System.out.println(comparator3.compare(33,44));
    }
  1. 举例:(o1,o2)->Integer.compare(o1,o2);

  2. 格式:

​ ->:lambda操作符 或 箭头操作符

​ ->左边:lambda形参列表(其实就是接口中的抽象方法的形参列表)

​ ->右边:lambda体(其实就是重写抽象方法的方法体)

  1. lambda表达式的使用(分6中情况介绍)

    ​ 总结:

    ​ ->左边:lambda形参列表的参数类型可以省略(类型推断),如果lambda形参列表只有一个参数,则小括号也可以省略

    ​ ->右边:lambda体应该用一对{}包裹,如果lambda体只有一条执行语句(可能是return语句),可以省略这一对{}和return关键字

  2. lambda表达式的本质:函数式接口的实现

  • 语法格式1:无参无返回值的(同上runnable)
  • 语法格式2:需要一个参数但是无返回值
@Test
    public void test2(){
        Consumer<String> con = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        con.accept("谎言和誓言的区别是什么呢");
        Consumer<String> con1 = (String s)->{
            System.out.println(s);
        };
        con.accept("没有区别");
    }
  • 语法格式3:lambda表达式中括号中的参数类型可以省略,可由表达式推断出,称之为‘类型推断’
@Test
    public void test3(){
        Consumer<String> con = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        con.accept("谎言和誓言的区别是什么呢");

        Consumer<String> con1 = (s)->{
            System.out.println(s);
        };
        con1.accept("一个听得人当真了,一个说的人当真了");
        ArrayList<String> array = new ArrayList<>();//类型推断
    }
  • 语法格式4:若只要一个参数,参数的 小括号可以省略
@Test
    public void test4(){
        Consumer<String> con = (s)->{
            System.out.println(s);
        };
        con.accept("一个听得人当真了,一个说的人当真了");

        Consumer<String> con1 = s->{
            System.out.println(s);
        };
        con1.accept("一个听得人当真了,一个说的人当真了");
    }
  • 语法格式5:lambda需要两个或以上的参数,多条执行语句,并且可以有返回值
@Test
    public void test5(){

        Comparator<Integer> com = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                System.out.println(o1);
                System.out.println(02);
                return o1.compareTo(o2);
            }
        };
        System.out.println(com.compare(45,54));

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


        Comparator<Integer> com1 = (o1,o2)->{
            System.out.println(o1);
            System.out.println(02);
            return o1.compareTo(o2);
        };
        System.out.println(com1.compare(12,21));
    }
  • 语法格式6:当lambda只有一条执行语句,若有大括号和return语句,都可以省略
@Test
    public void test6(){
        Comparator<Integer> com = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1.compareTo(o2);
            }
        };
        System.out.println(com.compare(15,25));
        System.out.println("**********");


        Comparator<Integer> com2 = (o1,o2)-> o1.compareTo(o2);
        System.out.println(com2.compare(4,1));
    }
    @Test
    public void test8(){
        Consumer<String> con = s-> System.out.println(s);
        con.accept("404");
    }

函数式接口

函数式接口:如果一个接口中只声明了一个抽象方法,则此接口为函数式接口。

自定义函数式接口

@FunctionalInterface //加上或者不加此接口都是函数式接口  只是用来检测是否为函数式接口  如果不是会报错
public interface myInterface {
    void mehtod1();
    //void mehtod1();
    
}

注:在java.util.function包下定义了丰富的函数式接口。

四大核心函数式接口

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

函数式接口举例

  • Consumer:消费型借口
@Test
    public void Test1(){
        happy(520.0,x-> System.out.println("学习太累了,做大保健花费"+x+"元"));
    }
    public void happy(Double money , Consumer<Double> consumer){
        consumer.accept(money);
    }
  • Supplier:供给型借口
@Test
    public void Test2(){
        String s = getList(() -> "函数式接口supplier");
        System.out.println(s);
    }
    public String getList(Supplier<String> supplier){
       return supplier.get();
    }
  • Predicate:断定型接口
@Test
    public void Test3(){
        List<String> list = Arrays.asList("东京", "东京", "天津", "天津", "北京", "北京");
        Set<String> stringSet = function(x -> {
            Set<String> set = new HashSet<>();
            set.addAll(x);
            return set;
        }, list);
        System.out.println(stringSet);

    }
    public Set<String> function(Function<List<String>,Set<String>> function,List<String> list){
            return function.apply(list);
    }
  • Funtion<T,R>函数型接口
@Test
    public void Test3(){
        List<String> list = Arrays.asList("北京","上海","南京","深圳");
        List<String> strings = filter(list, x -> x.contains("京"));
        System.out.println(strings);
    }
    //根据指定规则过滤集合中字符串,此规则由predicate方法决定
    public List<String> filter(List<String> list, Predicate<String> predicate){
        ArrayList<String> arrayList = new ArrayList<>();
        for(String l :list){
            if (predicate.test(l)){
               arrayList.add(l);
            }
        }
        return arrayList;
    }

由于对lambda表达式不熟悉以及函数式接口的陌生,以至于对上面这些小列子无法看懂无从下手:

步骤:

  1. 定义一个方法,方法参数为函数式接口、函数式接口的参数,返回值为函数式接口的返回值
  2. 方法中调用函数式接口的中的抽象方法(传入参数,如果没有则不传)。
  3. 调用自定义的方法,传入自定义方法的参数(函数式接口的实体类,以及函数式接口的需要的参数 )
  4. 传入函数式接口实体类使用lambda表达式

方法引用

方法引用的使用

  1. 使用情境:当传递给lambda体的操作,已经有实现的方法了,可以使用方法引用。
  2. 方法引用,本质上是lambda表达式,而lambda表达式作为函数式接口的实例,所以方法引用也就是函数式接口的实例
  3. 使用格式 类:(或对象)::方法名
  4. 具体分为如下三中情况:
    • 对象::非静态方法
    • 类::静态方法
    • 类::非静态方法
  5. 方法引用的使用要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法方法形参列表和返回值类型相同。(针对于情况1和情况2)

方法引用举例

employee类

public class Employee {
    protected  String id;
    protected  String name;
    protected  int age;
    protected  double salary;
    ……省略get set等
    }

情况一:

//情况1:对象::实例方法
    //Consumer中的accept(T t)
    //printStresam中的void print(T t)
    @Test
    public void Test(){
        Consumer<String> con = x-> System.out.println(x);
        con.accept("北京");
        System.out.println("************************");
        Consumer<String> consumer = System.out::println;
        consumer.accept("南京");
    }
//supplier中的T get()方法
    //Employ中String getName()方法
    @Test
    public void test2(){
        Employee emp = new Employee("10008", "张三", 45, 4550);
        Supplier<String> supplier1 = ()->emp.getName();
        String s1 = supplier1.get();
        System.out.println(s1);
        System.out.println("************************");
        Supplier<String> supplier2 = emp::getName;
        String s2 = supplier2.get();
        System.out.println(s2);
    }

情况二:

/情况二:类::静态方法
    //Comparator中int compare(T t1,T t2)
    //Integer中int compare(T t1,T t2)
    @Test
    public void test3(){
        Comparator<Integer> com1 = (x,y)->Integer.compare(x,y);
        System.out.println(com1.compare(45,456));
        System.out.println("*********************");
        Comparator<Integer> com2 = Integer::compare;
        System.out.println(com2.compare(78,56));
    }
//Function中的R apply(T t)
    //Math中的 Long round(Double b)
    @Test
    public void test4(){
        Function<Double,Long> function1 = x->Math.round(x);
        System.out.println(function1.apply(2.52));
        System.out.println("*****");
        Function<Double,Long> function2 = Math::round;
        System.out.println(function2.apply(2.666));
    }

情况三:

//情况三:类::实例方法(有难度)
    //Comparator中的int comaore(T t1,T t2
    // String 中的int ti.comapreTo(t2)
    @Test
    public void test5(){
        Comparator<String> comparator = (x1,x2)->x1.compareTo(x2);
        System.out.println(comparator.compare("abc","abd"));
        System.out.println("***************");

        Comparator<String> com = String::compareTo;
        System.out.println(com.compare("acb","acd"));
    }
//Bipredicate中的boolean test(T t1,T t2);
    //String中的boolean t1.equals(t2)
    @Test
    public void test6(){
        BiPredicate<String,String> predicate1 = (x1,x2)->x1.equals(x2);
        System.out.println(predicate1.test("sad","happy"));
        System.out.println("******************");

        BiPredicate<String,String> predicate2 = String::equals;
        System.out.println(predicate2.test("sos","sos"));
    }
//funtion中的R apply(T t)
    //Employee 中的getName();
    //apply中一个参数  方法引用相当于 t.getname();
    @Test
    public void test7(){
        Employee emp = new Employee("10008", "张三", 45, 4550);
        Function<Employee,String> function = x->x.getName();
        System.out.println(function.apply(emp));
        System.out.println("******************");

        Function<Employee,String> fun = Employee::getName;
        System.out.println(fun.apply(emp));
    }

构造器引用

构造器引用和方法引用类似,函数接口的方法的形参列表和构造器的形参列表一致,抽象方法返回值类型即为构造器所属的类

	//引用无参构造
	@Test
    public void test1(){
        Supplier<Employee> sup1 = ()-> new Employee();
        sup1.get();
        System.out.println("********************");
        Supplier<Employee> sup2 = Employee::new;
        sup2.get();
    }
    //引用单个参数构造器
    @Test
    public void test2(){
        Function<String,Employee> function1 = (x)->new Employee(x);
        function1.apply("zhangsan");
        System.out.println("********************");
        Function<String,Employee> function2 = Employee::new;
        function2.apply("zhangsan");
    }

数组的引用

可以把数组看成一个特殊的类,数组的引用与构造器引用类似

	//传入一个长度,返回该长度的一个数组
 	@Test
    public void test3(){
        Function<Integer,Double[]> function1 = x->new Double[x];
        System.out.println(Arrays.toString(function1.apply(5)));
        System.out.println("********************");
        Function<Integer,Double[]> function2 = Double[]::new;
        System.out.println(Arrays.toString(function1.apply(6)));
    }

StreamAPI概述(java.util.stream)

StreamAPI对数据集合进行操作,就类似于sql数据库查询。

为什么使用StreamAPI:

  • 实际开发中,项目中多数 数据源都来自于mysql、oracle等。但是现在数据源更多了,有 mongoDB,redis等,而这些非关系型数据库取出来的数据就需要java层去处理
  • Stream和Collection集合的区别:Collection是一种静态的内存数据结构,而Stream是有关计算的。前者主要是面向内存的,存储在内存中,后者是面向cpu的,通过cpu实现计算。

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

注意:

  1. stream不会自己存储数据
  2. Stream不会改变源对象。相反,他们返回持有一个结果的新Stream。
  3. Stream操作是延迟执行的。这意味他们需要用到结果时才会执行。

Stram操作的步骤:

  1. 创建Stream

一个数据源(如:数组、集合),获取一个流

  1. 中间操作

一个中间操作链,对数据源数据进行处理

  1. 终止操作

一旦执行终止操作,就执行中间操作链,产生结果。之后不再被使用。

Stream的中间操作

筛选与切片
方法描述
filter(Predicate p)接受lambda,从流中排除某些元素
distinct()筛选,通过 流所生成元素的hashCode()、equals()去除重复元素
limit(long maxSize)截断流,使其元素不超过指定的数量
skip(long n)跳过元素,返回一个去掉前n个元素的流,若流中元素不足n个,则返回一个空流,与limit互补
//筛选与切片
    @Test
     public void test1(){
        //filter(Predicate p) 接受lambda,从流中排除某些元素
        //输出员工表中薪资大于4000的
        List<Employee> employees = EmployeeData.employees();
        employees.stream().filter(x->x.getSalary()>4000).forEach(System.out::println);
        System.out.println("***********");
        //limit(long maxSize) 截断流,使其元素不超过指定的数量
        employees.stream().limit(3).forEach(System.out::println);
        System.out.println("***********");
        //  skip(long n)	跳过元素,返回一个去掉前n个元素的流,若流中元素不足n个,则返回一个空流,与limit互补
        employees.stream().limit(3).skip(1).forEach(System.out::println);
        //distinct()  筛选,通过 流所生成元素的hashCode()、equals()去除重复元素
        System.out.println("***********");
        employees.add(new Employee("1","1",1,1));
        employees.add(new Employee("1","1",1,1));
        employees.stream().distinct().forEach(System.out::println);
    }
映射
方法描述
map(Function f)接收一个函数为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
mapToDouble接收一个函数为参数,该函数会被应用到每个元素上,产生一个新的DoubleStream
mapToInt接收一个函数为参数,该函数会被应用到每个元素上,产生一个新的IntStream
mapToLong接收一个函数为参数,该函数会被应用到每个元素上,产生一个新的LongStream
flatMap接收一个函数为参数,将流中每个元素都换成另一个流,然后把所有流连接成令一个流
//映射
    @Test
    public void test2(){
        // map(Function f) 接收一个函数为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
        List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
        list.stream().map(x->x.toUpperCase()).forEach(System.out::println);
        //练习1:获取员工姓名大于2的姓名
        List<Employee> employees = EmployeeData.employees();
        Stream<String> nameStream = employees.stream().map(x -> x.getName());
        nameStream.filter(x->x.length()>2).forEach(System.out::println);
        //练习2:将字符串拆成单个字符输出
        Stream<Stream<Character>> stream = list.stream().map(str -> fromStringToStream(str));
//        stream.forEach(System.out::println); //Stream<Stream<Character>>
        stream.forEach(s->s.forEach(System.out::println));
        //flatMap  接收一个函数为参数,将流中每个元素都换成另一个流,然后把所有流连接成令一个流
        Stream<Character> characterStream = list.stream().flatMap(str -> fromStringToStream(str));
        characterStream.forEach(System.out::println);
        //map 和 flatMap一个返回Stream<Stream<Character>> 另一个返回Stream<Character>
    }
    //将字符串中多个字符构成的集合转换为Stream对应的实例
    public static Stream<Character> fromStringToStream(String str){
        ArrayList<Character> list = new ArrayList<>();
        for (Character c : str.toCharArray()) {
            list.add(c);
        }
        return list.stream();
    }
排序
方法描述
sorted()产生一个新流,按自然排序顺序排序
sorted(Comparator com)产生一个新流,按comparator中排序顺序进行排序
//排序
    @Test
    public void test4(){
        List<Integer> integers = Arrays.asList(1, 5, 98, 24, 36, -45, 7);
        integers.stream().sorted().forEach(System.out::println);
        System.out.println("***********");
        List<Employee> employees = EmployeeData.employees();
        //java.lang.ClassCastException: java2.Employee cannot be cast to java.lang.Comparable
        //抛异常,原因:employee没有实现comparable接口
        //employees.stream().sorted().forEach(System.out::println);
        employees.stream().sorted((x,y)->{
            int ageValue = Integer.compare(x.getAge(), y.getAge());
            if (ageValue!=0){
                return ageValue;
            }else {
                return -Double.compare(x.getSalary(),y.getSalary());
            }
        }).forEach(System.out::println);
终止操作
方法描述
allMathch(Predicate p)检查是否匹配全部元素
anyMathch(Predicate p)检查是否匹配一个元素
noneMatch(Predicate p)检查是否没有匹配所有元素
findFirst()返回第一个元素
findAny返回流中任何一个元素
count返回流中元素总个数
max(Comparator com)返回流中最大值
min(Comparetor com)返回流中最小值
foreach(Consumer c)内部迭代(使用Collection接口需要用户去迭代,称为外部迭代)。相反,StreamAPI使用内部迭代–帮你把迭代做了
   @Test
   public void test5(){
       List<Employee> employees = EmployeeData.employees();
       //allMathch(Predicate p) 检查是否匹配全部元素
       //员工是否都大于18岁
       boolean b = employees.stream().allMatch(x -> x.getAge() > 18);
       System.out.println(b);
       System.out.println("***********");
       //anyMathch(Predicate p) 检查是否匹配一个元素
       //是否存在工资大于20000的
       boolean b1 = employees.stream().anyMatch(x -> x.getSalary() > 20000);
       System.out.println(b1);
       System.out.println("***********");
       //noneMatch(Predicate p) 检查是否没有匹配所有元素
       //检查是否没有姓雷的
       boolean b2 = employees.stream().noneMatch(x -> x.getName().contains("雷"));
       System.out.println(b2);
       System.out.println("***********");
       //findFirst() 返回第一个元素
       //获取第一个员工
       Optional<Employee> first = employees.stream().findFirst();
       System.out.println(first);
       //findAny 返回流中任何一个元素
       //获取任意一个员工
       Optional<Employee> emp = employees.parallelStream().findAny();
       //Optional<Employee> emp = employees.stream().findAny();//总是获取第一个 因为stream是串行流,parallelStream()是并行流
       System.out.println(emp);
   }
@Test
    public void test6(){
        List<Employee> employees = EmployeeData.employees();
        //count 返回流中元素总个数
        long count = employees.stream().filter(employee->employee.getSalary()>5000).count();
        System.out.println(count);
        System.out.println("***********");
        //max(Comparator com) 返回流中最大值
        Optional<Double> max = employees.stream().map(x -> x.getSalary()).max(Double::compare);
        System.out.println(max);
        System.out.println("***********");
        //min(Comparetor com) 返回流中最小值
        Optional<Employee> min = employees.stream().min((e1, e2) -> {
            return Integer.compare(e1.getAge(), e2.getAge());
        });//返回年纪最小的员工信息
        //Optional<Integer> min = employees.stream().map(x -> x.getAge()).min((x, y) -> Integer.compare(x, y));//返回最小年纪
        System.out.println(min);
        System.out.println("***********");
        employees.stream().forEach(System.out::println);
    }
归约
方法描述
reduce(T iden,BinaryOperator)可以将流中的值反复结合起来,得到一个值,返回T
reduce(binaryOperator)可以将流中的值反复结合起来,得到一个值(),返回Optioal

第一个方法给予一个初始值 ,第二个方法法无初始值 即0

//归约
    @Test
    public void test7(){
        //reduce(T iden,BinaryOperator) 可以将流中的值反复结合起来,得到一个值,返回T
        List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        //计算1到10的和
        Integer reduce = integers.stream().reduce(0, (x, y) -> Integer.sum(x, y));//这里的0是一个初始值
        System.out.println(reduce);
        System.out.println("***********");

        //reduce(binaryOperator) 可以将流中的值反复结合起来,得到一个值(),返回Optioal<T>
        //计算员工工资大于3500 的员工工资总额和
        List<Employee> employees = EmployeeData.employees();
        Optional<Double> reduce1 = employees.stream().filter(employee -> employee.getSalary() > 3000).map(x -> x.getSalary()).reduce(Double::sum);
        System.out.println(reduce1);
        System.out.println("***********");
        //如果不知道Double中有sum方法 可以自己定义
        Optional<Double> reduce2 = employees.stream().map(x -> x.getSalary()).reduce((x, y) -> x + y);
        System.out.println(reduce2);
    }
收集
方法描述
collect(Collector c)将流转换为其他形式,接收一个Collector接口的实现,用于Stream中元素做汇总的方法
@Test
    public void test8(){
        //collect(Collector c) 将流转换为其他形式,接收一个Collector接口的实现,用于Stream中元素做汇总的方法
        //查找工资大于6000的员工名字,收集成一个set集合
        List<Employee> employees = EmployeeData.employees();
        Set<String> names = employees.stream().filter(x->x.getSalary()>6000).map(x -> x.getName()).collect(Collectors.toSet());
        System.out.println(names);
    }

toMap()

@Test
    public void test10(){
//        将Employee对象转换为id为key的map,value是Employee对象
        List<Employee> employees = EmployeeData.employees();
        Map<String, Employee> collect = employees.stream().collect(Collectors.toMap(Employee::getId, Function.identity()));
        System.out.println(collect);
        System.out.println("***********");
        employees.add(new Employee("1001","陶伟",21,2000.0));
        //如果有重复key值收集到map中,会报错
        Map<String, Employee> collect1 = employees.stream().collect(Collectors.toMap(Employee::getId, Function.identity()));
        System.out.println(collect1);
        System.out.println("***********");
        //假如id存在重复值,则会报错Duplicate key xxx, 解决方案是:
        // 只取前  一个key及value:
        Map<String, Employee> collect2 = employees.stream().collect(Collectors.toMap(Employee::getId, Function.identity(), (oldValue, newValue) -> oldValue));
        System.out.println(collect2);
        System.out.println("***********");
        //只取后一个key及value:
        Map<String, Employee> collect3 = employees.stream().collect(Collectors.toMap(Employee::getId, Function.identity(), (oldValue, newValue) -> newValue));
        System.out.println(collect3);
    }
@Test
    public void test11(){
        //想获得一个id和name对应的Map<String, String>
        List<Employee> employees = EmployeeData.employees();
        Map<String, String> collect = employees.stream().collect(Collectors.toMap(Employee::getId, Employee::getName));
        System.out.println(collect);
        //注意:name可以为空字符串但不能为null,否则会报空指针,解决方案:
        employees.add(new Employee("1001",null,21,2000.0));
        Map<String, String> collect1 = employees.stream().collect(Collectors.toMap(Employee::getId, x -> x.getName() == null ? "" : x.getName()));
        System.out.println(collect1);
        System.out.println("***********");
        //假如存在id重复,两个vaue可以这样映射到同一个id:
        Map<String, String> collect2 = employees.stream().collect(Collectors.toMap(Employee::getId, Employee::getName, (e1, e2) -> e1 + "" + e2));
        System.out.println(collect2);
        System.out.println("***********");
        //把Student集合按照group分组到map中
        Map<Integer, List<Employee>> collect3 = employees.stream().collect(Collectors.groupingBy(Employee::getAge));
        System.out.println(collect3);
        System.out.println("***********");
    }

Optional类(java.util.optional)

java是没有指针的,所以java中空指针异常情况中的“指针”是指的是java的引用,空指针就是空引用。java空指针异常就是引用本身为空,却调用了方法,这个时候就会出现空指针异常情况

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

optional提供很多有用的方法,这样我们就不用显式进行空指针检测

  • 创建optional类的方法
    • optional.of(T t):创建一个optional实例,t必须为非空
    • optional.empty():创建一个空的optional实例
    • optional.ofNullable(T t):t可以为空
  • 判断optional容器是否包含对象
    • boolean isPresent():判断是否包含对象
    • void ifPresent(Consumer con):如果有值,就会执行Consumer接口的实现代码,并且该值作为参数传给他
  • 获取optional容器的对象
    • T get() 如果调用对象包含值,则返回该值,否则抛出异常
    • T orElse(T other):如果有值则将其返回,否则返回指定的other对象
    • T orElseGet(Suppelier supplier):如果有值则将其返回,否则返回suppelier接口实现提供的对象
    • orElseThrow (Suppelier suppelier):如果有值则将其返回,否则抛出有supplier接口实现提供的异常
@Test
    public void test1(){
        Girl girl = new Girl();
//        girl=null;
        //of(T t )方法必须保证T是非空的
        Optional<Girl> girl1 = Optional.of(girl);
    }
    @Test
    public void test2(){
        Girl girl = new Girl();
        girl=null;
        Optional<Girl> optionalGirl = Optional.ofNullable(girl);//Optional.empty
        System.out.println(optionalGirl);
    }
 public String getGirlName(Boy boy){
        return boy.getGirl().getName();
    }
    
    @Test
    public void test3(){
        Boy boy = new Boy();
        //如果boy为null,调用在getGirlName()方法时 在boy.getGirl()时就会报错
//        boy=null;
		//当boy不为null,由于boy对象中girl为空,调用在getGirlName()方法时, 在girl.getName()时会报错
        String girlName = getGirlName(boy);
        System.out.println(girlName);
    }

对getGirl方法进行优化

 public String getGirlName1(Boy boy){
        if (boy!=null){
            if (boy.getGirl()!=null){
                return boy.getGirl().getName();
            }else {
                return "girl为空";
            }
        }
        return "boy为空";
    }
    @Test
    public void test4(){
        Boy boy = new Boy();
//        boy=null;
        String girlName = getGirlName1(boy);
        System.out.println(girlName);
    }

optional.orelse()使用

 @Test
    public void test5(){
        Girl girl = new Girl();
//        girl = null;
        Optional<Girl> girl1 = Optional.ofNullable(girl);
        System.out.println(girl1);
        Girl girl2 = girl1.orElse(new Girl("张三"));
        //orElse(T t1):如果当前optional内部封装的t是非空的,则返回内部的t,
        //如果内部的t是空的,则返回orelse()方法中的参数T
        System.out.println(girl2);
    }

使用optional类的getGirlName(Boy boy)

 public String getGirlName2(Boy boy){
        Optional<Boy> boy1 = Optional.ofNullable(boy);
        Boy boy2 = boy1.orElse(new Boy(new Girl("zhangsan")));
        Girl girl = boy2.getGirl();
        Optional<Girl> girl1 = Optional.ofNullable(girl);
        Girl girl2 = girl1.orElse(new Girl("lisi"));
        return girl2.getName();
    }
    @Test
    public void test6(){
        Boy boy = new Boy();
//        boy = null;
		Boy boy = new Boy(new Girl("苍老师"));
        String girlName2 = getGirlName2(boy);
        System.out.println(girlName2);
    }

视频地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值