JDK8的特性

1.Lambda表达式

1.1 lambda的由来

public class Test01 {
    public static void main(String[] args) {
        //开启一个线程 该构造函数需要传递一个Runnable类型的接口参数
        Thread thread = new Thread(new My());
        thread.start();//开启线程
        //匿名内部类
        Thread thread1=new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("通过匿名内部类实现线程任务");
            }
        });
        thread1.start();//开启线程

    }
}
class My implements  Runnable{

    @Override
    public void run() {
        System.out.println("使用实现类来完成------->线程任务");
    }
}

分析:

  1. Thread类需要一个Runnable接口作为参数,其中的抽象方法run方法是用来指定线程任务内容的核心
  2. 为了指定run方法体,不得不需要Runnable的实现类
  3. 为了省去定义一个Runnable 的实现类,不得不使用匿名内部类
  4. 必须覆盖重写抽象的run方法,所有的方法名称,方法参数,方法返回值不得不都重写一遍,而且不能出错,
  5. 而实际上,我们只在乎方法体中的代码.
    我们可以使用lambda表达式来完成上面的功能。

1.2 初体验lambda表达式。

//lambda表达式.
Runnable runnable1=()->{
System.out.println(“这是lambda表达式完成线程任务”);
};
Thread thread2=new Thread(runnable1);
thread2.start();

1.3 lambda表达式的语法

Lambda省去了面向对象的条条框框,Lambda的标准格式由3个部分组成:

(参数列表)->{}
():参数列表
->:连接符 连接的是参数以及方法体。
{}: 方法体。

1.4 练习无参无返回值的Lambda

public class Test02 {
    public static void main(String[] args) {
          //主函数调用fun方法。第一种:创建UserService接口的实现类,并创建该实现类对象。
          //第二种: 匿名内部类的方式
//          UserService userService=new UserService() {
//              @Override
//              public void show() {
//                  System.out.println("这是匿名内部类的show方法的实现");
//              }
//          };
//          fun(userService);
          //第三种lambda表达式:--该接口必须为函数式接口
         UserService userService=()->{
             System.out.println("lambda表示的show方法");
         };
         fun(userService);
    }
    public static void fun(UserService userService){//UserService userService=
        userService.show();
    }
}
//函数式接口-->里面有且仅有一个抽象方法。--只有这种接口才能使用lambda表达式。
interface UserService{
    public void show();
}

1.5 有参有返回值。

public class Test03 {
    public static void main(String[] args) {
        List<Person> personList=new ArrayList<>();
        personList.add(new Person("张三",15));
        personList.add(new Person("李四",5));
        personList.add(new Person("王五",65));
        personList.add(new Person("赵六",45));
        personList.add(new Person("田七",35));
        //对集合中的元素按照年龄排序。小到大
        System.out.println(personList);
        //Collections:集合工具类。--匿名内部类
//        Comparator<Person> comparator=new Comparator<Person>() {
//            @Override //如果是0表示相同 大于0表示o1大于02
//            public int compare(Person o1, Person o2) {
//                return o1.getAge()-o2.getAge();
//            }
//        };
//        Collections.sort(personList,comparator);
//        System.out.println(personList);
        Comparator<Person> comparator=(o1,o2)->{ 
            //就是对函数式接口中抽象方法的简写。
          return o1.getAge()-o2.getAge();
        };
          Collections.sort(personList,comparator);
          System.out.println(personList);
    }
}
class Person{
     private String name;
     private Integer age;

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public Person() {
    }

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

1.6 Lambda表达式的省略写法

在lambda表达式的标准写法基础上,可以使用省略写法的规则为:

  1. 小括号内的参数类型可以省略[]
  2. 如果小括号内有且仅有一个参数,则小括号可以省略
  3. 如果大括号内有且仅有一个语句,可以同时省略大括号,return 关键字及语句分号。
public class Test05 {
    public static void main(String[] args) {
        USB u=(str)-> str.toUpperCase();//lambda表达式:
//        USB u=new USB() {//匿名内部类对接口中抽象方法的个数没有任何要求.
//            @Override
//            public String toUpper(String str) {
//                return null;
//            }
//
//            @Override
//            public String toLower(String str) {
//                return null;
//            }
//        };
        fun(u);
    }
    public static void fun(USB usb){
        String s = usb.toUpper("hello");
        System.out.println(s);
    }
}
interface USB{
    public String toUpper(String str);
//    public String toLower(String str);
}

1.7 Lambda表达式使用的前提

Lambda表达式的语法是非常简洁的,但是Lambda表达式不是随便使用的,使用时有几个条件要特别注意

  1. 方法的参数或局部变量类型必须为接口才能使用Lambda
  2. 接口中有且仅有一个抽象方法(@FunctionalInterface)

后面我们使用lambda表达式主要核心使用再Stream流中。

2.内置函数式接口

要想使用lambda表达式它的前提就是必须是函数式接口。

2.1 内置函数式接口的由来

public class Test {

    public static void main(String[] args) {
           Operation o=arr->{
               int sum=0;
               for(int s:arr){
                   sum+=s;
               }
               return sum;
           };
            fun(o);
    }
    public static void fun(Operation operation){
        int[] arr={1,2,3,4};
        int s = operation.getSum(arr);
        System.out.println("数组的和:"+s);
    }
}
@FunctionalInterface
interface Operation{
     public int getSum(int[] arr);
}

分析:
我们知道使用Lambda表达式的前提是需要有函数式接口,而Lambda表达式使用时不关心接口名,抽象方法名。只关心抽象方法的参数列表和返回值类型。因此为了让我们使用Lambda表达式更加的方便,在JDK中提供了大量常用的函数式接口. 大多数无需自己再定义函数式接口,而可以直接使用jdk内置的函数式接口。分成四类。

在这里插入图片描述

2.2 消费型函数式接口Consumer

适合有参数,但是没有返回值的。

public class Test06 {
    public static void main(String[] args) {
        Consumer<Double> c=t->{
            System.out.println("今天洗脚花费:"+t+"元");
        };
        fun(c,200.0);
    }
    public static void fun(Consumer<Double> consumer,Double money){
          consumer.accept(money);
    }
}

2.3 供给型函数式接口—Supplier

无参,需要返回值的接口类。

public class Test06 {
    public static void main(String[] args) {
//        Consumer<Double> c=t->{
//            System.out.println("今天洗脚花费:"+t+"元");
//        };
//        fun(c,200.0);
         Supplier<Integer> s=()-> new Random().nextInt(10);
         fun2(s);
    }
    public static void fun2(Supplier<Integer> supplier){
        Integer a = supplier.get();
        System.out.println("结果:"+a);
    }


    public static void fun(Consumer<Double> consumer,Double money){
          consumer.accept(money);
    }
}

2.3 函数型函数式接口—Function<T,R>

T: 参数的泛型
R:返回值的泛型。

public class Test {

    public static void main(String[] args) {
           Function<int[],Integer> o=arr->{
               int sum=0;
               for(int s:arr){
                   sum+=s;
               }
               return sum;
           };
            fun(o);
    }
    public static void fun(Function<int[],Integer> fun){
        int[] arr={1,2,3,4};
        int s = fun.apply(arr);
        System.out.println("数组的和:"+s);
    }
}

2.4 断言型函数式接口–Predicate

T: 参数
boolean:返回值类型。

public class Test06 {
    public static void main(String[] args) {
//        Consumer<Double> c=t->{
//            System.out.println("今天洗脚花费:"+t+"元");
//        };
//        fun(c,200.0);
//         Supplier<Integer> s=()-> new Random().nextInt(10);
//         fun2(s);
//          Function<String,String> fun=t->t.toUpperCase();
//          fun3(fun,"hello world");
          Predicate<String> p=t->t.length()>3;
          fun4(p,"欧阳锋"); //4
    }

    public static void fun4(Predicate<String> predicate,String name){
        boolean test = predicate.test(name);
        System.out.println("是否成年:"+test);
    }

    public static void fun3(Function<String,String> f,String str){
        String apply = f.apply(str);
        System.out.println("结果:"+apply);
    }
    public static void fun2(Supplier<Integer> supplier){
        Integer a = supplier.get();
        System.out.println("结果:"+a);
    }

    public static void fun(Consumer<Double> consumer,Double money){
          consumer.accept(money);
    }
}

3.方法引用

特殊的lambda表达式,它是对lambda表达式的一种简写方式。

3.1 方法引用的由来

public class Test07 {
    public static void main(String[] args) {
           Consumer<int[]> c=t->{
                int sum=0;
                for(int a:t){
                    sum+=a;
                }
               System.out.println("数组的和:"+sum);
           };
           fun(c);
    }
    public static void fun(Consumer<int[]> consumer){
          int[] arr={1,2,3,4,5};
          consumer.accept(arr);
    }

    //求和方法
    public static void sum(int[] arr){
        int sum=0;
        for(int a:arr){
            sum+=a;
        }
        System.out.println("数组的和:"+sum);
    }
}

如果我们在Lambda中所指定的功能,已经有其他方法存在相同方案,那是否还有必要再写重复逻辑?可以直接“引 用”过去就好了:—方法引用。::

public class Test07 {
    public static void main(String[] args) {
//           Consumer<int[]> c=t->{
//                int sum=0;
//                for(int a:t){
//                    sum+=a;
//                }
//               System.out.println("数组的和:"+sum);
//           };
//        Consumer<int[]> c=(t)->Test07.sum(t); //
           Consumer<int[]> c= Test07::sum;
           fun(c);
    }
    public static void fun(Consumer<int[]> consumer){
          int[] arr={1,2,3,4,5};
          consumer.accept(arr);
    }

    //求和方法
    public static void sum(int[] arr){
        int sum=0;
        for(int a:arr){
            sum+=a;
        }
        System.out.println("数组的和:"+sum);
    }
}

3.2 方法引用的类型

在这里插入图片描述

方法引用是lambda表达式的一种简写形式。如果lambda表达式中调用一个特定已经存在方法,则可以使用方法引用。

3.3 静态方法引用

(args)->类名.静态方法(args). 当lambda表达式中方法体,只有一条语句,而这条语句是类名.静态方法。而静态方法的参数和lambda的参数一致时。
类名::静态方法;

3.4 实例方法引用

(args) -> inst.instMethod(args)
实例方法引用,顾名思义就是调用已经存在的实例的方法,与静态方法引用不同的是类要先实例化,静态方法引用类无需实例化,直接用类名去调用。

public class Test09 {
    public static void main(String[] args) {
        //创建一个类对象
        Student s=new Student("赵志强",17);

        //通过内置的函数接口,返回对象的名称。
//        Supplier<String> supplier=()->s.getName();
        Supplier<String> supplier=s::getName;
        String s1 = supplier.get();
        System.out.println(s1);
        //观察: lambda表达式中有且仅有一条语句,方法调用调用语句。 ---实例方法引用特点: ()->对象.普通方法();
        Function<Student,String> f=(t)->t.getName();

        String apply = f.apply(s);
        System.out.println(apply); //这里就无法使用方法引用。

    }
}
class Student{
     private String name;
     private Integer age;//实体类中不能出现基本类型

    public Student(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

3.5 对象方法引用

lambda: (inst,args)->inst.普通方法(args): -------->类名::普通方法
若Lambda参数列表中的第一个参数是实例方法的参数调用者,而第二个参数是实例方法的参数时,可以使用对象方法引用。

public class Test10 {
    public static void main(String[] args) {
        //判断两个字符串是否一致。
//        BiFunction<String,String,Boolean> biFunction=(t1,t2)->t1.equals(t2);
//        Boolean apply = biFunction.apply("hello", "hello");
//        System.out.println(apply);
        //观察:符合对象方法引用.----代码---进入公司---git拉取代码【看】---看懂
        BiFunction<String,String,Boolean> biFunction=String::equals;
    }
    public static void show(BiFunction<String,String,Boolean> biFunction){
        Boolean apply = biFunction.apply("hello", "hello");
        System.out.println(apply);
    }
}

3.6 构造方法引用

(args) -> new 类名(args)------构造方法引用: 类名::new

public class Test11 {
    public static void main(String[] args) {
        //        Supplier<Student> supplier=()->new Student();
        //        Student student = supplier.get();
        //        System.out.println(student);
        //观察:调用的构造函数.
        //        Supplier<Student> supplier=Student::new;
        //        Student student = supplier.get();
        //        System.out.println(student);
        //        BiFunction<String,Integer,Student> biFunction=(n,a)->new Student(n,a);
        BiFunction<String,Integer,Student> biFunction=Student::new;
        Student student = biFunction.apply("霍梦琪", 25);
        System.out.println(student);
    }
}

总结:

静态方法引用 类名::静态方法 lambda表达式: (参数)->类名.静态方法(参数)
实例方法引用:对象::实例方法 lambda表达式: (参数)->对象.实例方法(参数)
对象方法引用:类名::实例方法 lambda表达式: (参数1,参数2…)->参数1.实例方法(参数2…)
构造方法引用: 类名::new lambda表达式: (参数)->new 类名(参数);

4.Stream

Java8的两个重大改变,一个是Lambda表达式,另一个就是本节要讲的Stream API表达式。Stream 是Java8中处理集合的关键抽象概念,它可以对集合进行非常复杂的查找、过滤、筛选等操作.

4.1 为什么使用stream流

当我们需要对集合中的元素进行操作的时候,除了必需的添加、删除、获取外,最典型的就是集合遍历。我们来体验 集合操作数据的弊端,需求如下:

一个ArrayList集合中存储有以下数据:张无忌,周芷若,赵敏,成俊杰,张三丰
需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据

public class Test {
    public static void main(String[] args) {
//        一个ArrayList集合中存储有以下数据:张无忌,周芷若,赵敏,成俊杰,张三丰

//        需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据
        List<String> list=new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("程俊杰");
        list.add("张三丰");

        //1.拿到所有姓张的
        List<String> newList01=new ArrayList<>();
        for(String n:list){
            if(n.startsWith("张")){
                 newList01.add(n);
            }
        }

        //2.拿到名字长度为3个字的
        List<String> newList02=new ArrayList<>();
        for(String n:newList01){
             if(n.length()==3){
                 newList02.add(n);
             }
        }

        //3.打印这些数据
        for(String s:newList02){
            System.out.println(s);
        }


    }
}

分析:

循环遍历的弊端
这段代码中含有三个循环,每一个作用不同:

  1. 首先筛选所有姓张的人;
  2. 然后筛选名字有三个字的人;
  3. 最后进行对结果进行打印输出。

每当我们需要对集合中的元素进行操作的时候,总是需要进行循环、循环、再循环。这是理所当然的么?不是。循环 是做事情的方式,而不是目的。每个需求都要循环一次,还要搞一个新集合来装数据,如果希望再次遍历,只能再使 用另一个循环从头开始。
那Stream能给我们带来怎样更加优雅的写法呢?

Stream初体验

List<String> list=new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张三");
        list.add("程俊杰");
        list.add("张三丰");

        //
list.stream().filter(t->t.startsWith("张")).filter(t->t.length()==3).forEach(item-> System.out.println(item));

4.2 Stream流的原理

Stream流式思想类似于工厂车间的“生产流水线”,Stream流不是一种数据结构,不保存数据,而是对数据进行加工 处理。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。
在这里插入图片描述

4.3 步骤

(1)获取Stream流对象

(2) 中间操作—返回类型还是Stream流对象。

(3)终止操作—不在是Stream流对象
在这里插入图片描述

4.4 获取Stream流对象的方式

(1) 通过集合对象调用stream()
(2)通过Arrays获取stream流对象
(3)通过Stream流里面of方法

public class Test11 {
    public static void main(String[] args) {
        List<String> list=new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
//        Stream<String> stream = list.stream();
//        stream.forEach(System.out::println);
//        //第二种使用Arrays工具类
//        String[] arr={};
//        Stream<String> stream1 = Arrays.stream(arr);
//
//        //第三种:Stream类
//        Stream<Integer> stream2 = Stream.of(1, 2, 4, 7, 8);
        //上面的流都是串行流。并行流
        Stream<String> stringStream = list.parallelStream();
        stringStream.forEach(System.out::println);
    }
}

4.5 Stream流的api方法

举个简单的例子:
假设有一个Person类和一个Person列表,现在有两个需求:1)找到年龄大于18岁的人并输出;2)找出所有中国人的数量。

class Person {
    private String name;
    private Integer age;
    private String country;
    private char sex;

    public Person(String name, Integer age, String country, char sex) {
        this.name = name;
        this.age = age;
        this.country = country;
        this.sex = sex;
    }
}
public class Test {
    public static void main(String[] args) {
        List<Person> personList = new ArrayList<>();
        personList.add(new Person("欧阳雪",18,"中国",'F'));
        personList.add(new Person("Tom",24,"美国",'M'));
        personList.add(new Person("Harley",22,"英国",'F'));
        personList.add(new Person("向天笑",20,"中国",'M'));
        personList.add(new Person("李康",22,"中国",'M'));
        personList.add(new Person("小梅",20,"中国",'F'));
        personList.add(new Person("何雪",21,"中国",'F'));
        personList.add(new Person("李康",22,"中国",'M'));
        //1. 年龄大于18  filter:过滤掉不满足条件的元素.  forEach:输出元素. ---如果没有终止函数,那么中间函数的代码不会被执行。
        personList.stream(). filter(item->{
            System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~");
            return item.getAge()>18;
        }).forEach(System.out::println);

        //2. 找出中国人  并统计个数: count()
        long count = personList.stream().filter(item -> item.getCountry().equals("中国")).count();
        System.out.println("中国人:"+count);
    }
}

2)找出年龄最大和最小

Person person = personList.stream().max(((o1, o2) -> o1.getAge() - o2.getAge())).get();
        Person person2 = personList.stream().min(((o1, o2) -> o1.getAge() - o2.getAge())).get();
        System.out.println(person);
        System.out.println(person2);

(3)map–>

会把集合中的元素转化成另一种类型

在这里插入图片描述

personList.stream().filter(item->item.getCountry().equals("中国")).map(item->new P(item.getName(),item.getAge())).forEach(System.out::println);
public class Test03 {
    public static void main(String[] args) {
//        整数数组每个元素+3
        List<Integer> list = Arrays.asList(1, 17, 27, 7);
        list.stream().map(item->item+3).forEach(System.out::println);

        List<String> list2=Arrays.asList("hello","world","java","spring","springmvc");
        //字符串大写
        list2.stream().map(String::toUpperCase).forEach(System.out::println);
    }
}

(4)收集 collect

把处理过的集合搜集成新的集合。

List<Person> personList = new ArrayList<>();
        personList.add(new Person("小梅",24,"中国",'F'));
        personList.add(new Person("欧阳雪",18,"中国",'F'));
        personList.add(new Person("Tom",24,"美国",'M'));
        personList.add(new Person("Harley",22,"英国",'F'));
        personList.add(new Person("向天笑",20,"中国",'M'));
        personList.add(new Person("李康",22,"中国",'M'));
        personList.add(new Person("Tom",21,"中国",'F'));
        personList.add(new Person("李康",22,"中国",'M'));

        //把Person-年龄大于20人--里面名称----新的集合。
        List<String> collect = personList.stream().filter(item -> item.getAge() > 20).map(item -> item.getName()).collect(Collectors.toList());
        System.out.println(collect);

(5)sorted排序

List<Person> collect = personList.stream().sorted((o1, o2) -> o1.getAge() - o2.getAge()).collect(Collectors.toList());
        System.out.println(collect);

(6) reduce规约

归约,也称缩减,顾名思义,是把一个流缩减成一个值,能实现对集合求和、求乘积和求最值操作。
在这里插入图片描述
整型集合: -----请求。[1,2,3,4]=

List<Integer> list= Arrays.asList(1,2,3,5);Optional<Integer> reduce = list.stream().reduce((t1, t2) -> t1 * t2);//t1=1, t2=2 ===>2//t1=2  t2=3===>6//t1=6  t2=5====>30System.out.println(reduce.get());

(7)查询第一个findFirst

 Optional<Person> first = personList.stream().filter(item->item.getAge()>=18&&item.getAge()<=20).findFirst();
        System.out.println(first.get());
  1. 中间的操作: filter map sorted distinct() skip limit()
  2. 终止操作: forEach count() reduce() collect(Collectors.toList()) findFirst
    max min
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值