JDK8的特性详细介绍及运用

本文详细介绍了JDK8中的重要特性,包括Lambda表达式的由来、语法、使用前提和各种类型,如无参无返回值、有参有返回值的Lambda,以及Lambda的省略写法。此外,文章还阐述了内置的函数式接口,如Consumer、Supplier、Function和Predicate,并通过实例展示了方法引用的不同类型,如静态方法引用、实例方法引用等。最后,文章深入讲解了Stream流的概念、原理、操作步骤,以及如何使用StreamAPI进行数据处理,如过滤、聚合、映射和收集等操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1 jdk8的特性

2 Lambda表达式

2.1 Lambda的由来

分析:

2.2 初体验lambda表达式

2.3 lambda表达式的语法

2.4 Lambda表达式使用的前提

2.5 无参无返回值的Lambda表达式

2.6 有参有返回值的Lambda表达式

2.7 Lambda表达式的省略写法

3. 内置函数式接口

3.1 内置函数式接口的由来

 3.2 消费型函数式接口---Consumer

3.3 供给型函数式接口---Supplier

3.4 函数型函数式接口---Function,r>

3.5 断言型函数式接口--Predicate

4. 方法引用

4.1 方法引用的类型

4.2 静态方法引用

4.3 实例方法引用

4.4 对象方法引用

4.5 构造方法引用

5. Stream流

5.1 Stream初体验

5.2 Stream流的原理

5.3 步骤

5.4 获取Stream流对象的方式

5.5 Stream流中的api方法

5.5.1 遍历/过滤/(foreach/filter)

5.5.2 聚合(count、max、min)

5.5.3 map

5.5.4 collect(收集)

5.5.5 sorted(排序)

5.5.6 reduce规约

5.5.7 findFirst(查找第一个元素)

5.5.8 提取、组合


1 jdk8的特性

  • Lambda表达式(主要使用在Stream流中)
  • 函数式接口
  • 方法引用
  • Stream流
  • 日期时间类

2 Lambda表达式

2.1 Lambda的由来

public class Test02 {
    public static void main(String[] args) {
        //开启一个线程 该构造函数需要传递一个Runnable类型的接口参数
        Thread thread = new Thread(new MyThread());
        thread.start();//开启线程

        //匿名内部类
       Runnable runnable = new Runnable(){
           @Override
           public void run() {
               System.out.println("通过匿名内部类完成线程任务");
           }
       };
        Thread thread1 = new Thread(runnable);
        thread1.start();//开启线程
    }
}
class MyThread implements Runnable{
    @Override
    public void run() {
        System.out.println("使用实现类来完成--------->线程任务");
    }
}

分析:

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

2.2 初体验lambda表达式

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

2.3 lambda表达式的语法

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

(参数列表)->{}

  • ():参数列表。

  • ->:连接符,连接的是参数以及方法体。

  • {}:方法体。

2.4 Lambda表达式使用的前提

1.方法的参数或局部变量类型必须为接口才能使用Lambda。

2.接口中有且仅有一个抽象方法(@FunctionalInterface)。

2.5 无参无返回值的Lambda表达式

public class Test02 {
    public static void main(String[] args) {
        //lambda表达式:该接口必须为函数式接口
        UserService userService=()->{
            System.out.println("lambda表示的show方法");
        };
        fun(userService);
    }
    public static void fun(UserService userService){
        userService.show();
    }
}
//函数式接口--->里面有且仅有一个抽象方法。--->只有这种接口才可以是使用lambda表达式
interface UserService{
    public void show();
}

2.6 有参有返回值的Lambda表达式

public class Test03 {
    public static void main(String[] args) {
        List<Person> personList = new ArrayList<>();
        personList.add(new Person("张三丰",65));
        personList.add(new Person("任盈盈",23));
        personList.add(new Person("令狐冲",32));
        personList.add(new Person("林平之",12));
        personList.add(new Person("风清扬",88));
        //对集合中的元素按照年龄排序,从小到大
        System.out.println(personList);
        //Collections:集合类工具。--匿名内部类
       /* Comparator<Person> comparator = new Comparator<Person>() {
            @Override//如果是0表示相同 大于0表示o1>o2
            public int compare(Person o1, Person o2) {
                return o1.getAge()- o2.getAge();
            }
        };
        Collections.sort(personList,comparator);
        System.out.println(personList);*/
        //lambda表达式
        Comparator<Person> comparator =(o1,o2)->{
            //对函数式接口中抽象方法的简写。
            return o1.getAge() - o2.getAge();
        };
        Collections.sort(personList,comparator);
        System.out.println(personList);
    }
}
class Person{
    private String name;
    private int age;

    public Person() {
    }

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

    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;
    }

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

2.7 Lambda表达式的省略写法

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

  • 小括号内的参数类型可以省略。
  • 如果小括号内有且仅有一个参数,则小括号可以省略。
  • 如果大括号内有且仅有一个语句,可以同时省略大括号,return 关键字及语句分号。

代码演示如下:

public class Test04 {
    public static void main(String[] args) {

        //匿名内部类对接口中抽象方法的个数没有任何要求。
        /*USB u = new USB() {
            @Override
            public String toUpper(String str) {
                return str.toUpperCase();
            }
        };*/
        //lambda表达式
        USB u = str->str.toUpperCase();
        fun(u);
    }

    public static void fun(USB usb) {
        String s = usb.toUpper("hello world");
        System.out.println(s);
    }
}
interface USB{
    public String toUpper(String str);
}

3. 内置函数式接口

3.1 内置函数式接口的由来

public class Test05 {
    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 sum = operation.getSum(arr);
        System.out.println("数组的和:"+sum);
    }
}
@FunctionalInterface
interface Operation{
    public int getSum(int[] arr);
}

分析:

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

 3.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);
    }
}

3.3 供给型函数式接口---Supplier

适合无参,有返回值的接口类。

public class Test06 {
    public static void main(String[] args) {
       //供给型函数式接口
        Supplier<Integer> s = ()->new Random().nextInt(10);
        fun2(s);
    }
    public static void fun2(Supplier<Integer> supplier){
        Integer a = supplier.get();
        System.out.println("这期彩票的中奖号码为:"+a);
    }
}

3.4 函数型函数式接口---Function<T,R>

适合有参数,有返回值的。

T:表示参数的泛型。

R:表示返回值的泛型。

public class Test06 {
    public static void main(String[] args) {
        //函数型函数式接口
        Function<int[],Integer> f = arr->{
            int sum = 0;
            for (int b: arr){
                sum+=b;
            }
            return sum;
        };
        fun3(f);
 
    }
    public static void fun3(Function<int[],Integer> fun){
        int[] arr = {1,2,3,4};
        Integer sum = fun.apply(arr);
        System.out.println("数组的和为:"+sum);
    }
}

3.5 断言型函数式接口--Predicate

适合有参数,返回值类型为boolean的接口类。

T:表示参数类型。

boolean:表示返回值类型。

public class Test06 {
    public static void main(String[] args) {
       //断言型函数式接口
        Predicate<Integer> p = age->age>18;
        fun4(p,19);
    }
    public static void fun4(Predicate<Integer> predicate,Integer age){
        boolean test = predicate.test(age);
        System.out.println("是否成年:"+test);
    }
}

4. 方法引用

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

4.1 方法引用的类型

4.2 静态方法引用

lambda表达式:(args)->类名.静态方法(args)-------->类名::静态方法;

当lambda表达式中方法体,只有一条语句,而这条语句是类名.静态方法。而静态方法的参数和lambda的参数一致时可以用静态方法引用。

public class Test07 {
    public static void main(String[] args) {
        Consumer<int[]> c = arr->Test07.sum(arr);
        fun(c);
        //静态方法引用
        Consumer<int[]> con = Test07::sum;
        fun(con);

    }
     public static void fun(Consumer<int[]> consumer){
        int[] arr = {1,2,3,4};
        consumer.accept(arr);
     }
    //求和方法
    public static void sum(int[] arr){
        int sum=0;
        for(int a:arr){
            sum+=a;
        }
        System.out.println("数组的和:"+sum);
    }
}

4.3 实例方法引用

lambda表达式:(args) -> inst.instMethod(args)-------->对象名::实例方法;

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

public class Test08 {
    public static void main(String[] args) {
        //创建一个类对象
        Student student = new Student("杨玉环",18);

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

    public Student() {
    }

    public Student(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;
    }

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

4.4 对象方法引用

lambda表达式: (inst,args)->inst.普通方法(args) -------->类名::普通方法;

若Lambda参数列表中的第一个参数是实例方法的参数调用者,而第二个参数是实例方法的参数时,可以使用对象方法引用。

public class Test09 {
    public static void main(String[] args) {
        //判断两个字符串是否相等
        //BiFunction<String,String,Boolean> biFunction=(a,b)->a.equals(b);
        //对象方法引用
        BiFunction<String,String,Boolean> biFunction=String::equals;
        show(biFunction);
    }
    public static void show(BiFunction<String,String,Boolean> biFunction){
        Boolean apply = biFunction.apply("hello", "hello");
        System.out.println("两字符串是否相等:"+apply);
    }
}

4.5 构造方法引用

lambda表达式:(args) -> new 类名(args)------>类名::new;

public class Test10 {
    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 = (a,b)->new Student(a,b);
        BiFunction<String,Integer,Student> biFunction = Student::new;
        Student s = biFunction.apply("小龙女", 18);
        System.out.println(s);
    }
}
class Student{
    private String name;
    private Integer age;

    public Student() {
    }

    public Student(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;
    }

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

5. Stream流

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

5.1 Stream初体验

一个ArrayList集合中存储有以下数据:张无忌,周芷若,赵敏,张飞,张三丰。

需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据

public class Test11 {
    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("张三丰");

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

5.2 Stream流的原理

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

5.3 步骤

  1. 获取Stream流对象。
  2. 中间操作----返回类型还是Stream流对象。
  3. 终止操作----不再是Stream流对象。

5.4 获取Stream流对象的方式

  1. 通过集合对象调用stream()。

  2. 通过Arrays获取stream流对象。

  3. 通过Stream流里面of方法。

public class Test12 {
    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, 3, 4, 5, 6);
        //上面的流都是串行流。并行流如下:
        Stream<String> stringStream = list.parallelStream();
        stringStream.forEach(System.out::println);

    }
}

5.5 Stream流中的api方法

举个例子:

假设有一个Person类和一个Person列表,现在有两个需求:

1)找到年龄大于18岁的人并输出;

2)找出所有中国人的数量。

@Data
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;
    }
}

5.5.1 遍历/过滤/(foreach/filter)

public class Test13 {
    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(t->t.getAge()>18).forEach(System.out::println);
    }
}

5.5.2 聚合(count、max、min)

public class Test13 {
    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'));

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

        //找出年龄最大的人
        Person maxAge = personList.stream().max(((o1, o2) -> o1.getAge() - o2.getAge())).get();
        System.out.println(maxAge);

        //找出年龄最小的人
        Person minAge = personList.stream().min(((o1, o2) -> o1.getAge() - o2.getAge())).get();
        System.out.println(minAge);


    }
}

5.5.3 map

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

 获取集合中中国人的姓名和年龄

public class Test13 {
    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'));

       //map
        personList.stream().filter(t->t.getCountry().equals("中国")).map(item->new People(item.getName(), item.getAge())).forEach(System.out::println);
        
    }
}

(1)整数数组中每个元素+5

(2数组中字符串变为大写

public class Test01 {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 17, 27, 7);

        //整数数组每个元素+5
        list.stream().map(item->item+5).forEach(System.out::println);

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

5.5.4 collect(收集)

把处理过的集合收集成为新的集合。

需求:把Person-年龄大于20人--里面名称----收集为新的集合。

public class Test1 {
    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'));

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

5.5.5 sorted(排序)

需求:把集合中的人按照年龄从小到大排序

public class Test1 {
    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'));

        //把集合中的人按照年龄从大到小排序
        List<Person> collect1 = personList.stream().sorted(((o1, o2) -> o1.getAge() - o2.getAge())).collect(Collectors.toList());
        System.out.println(collect1);
    }
}

5.5.6 reduce规约

归约,也称缩减,顾名思义,是把一个流缩减成一个值,能实现对集合求和、求乘积和求最值操作。

 需求:求整型集合中元素的和、最大值、积

public class Test02 {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1,3,5,7);
        //求和
        Optional<Integer> sum = list.stream().reduce((t1, t2) -> t1 + t2);
        //t1=1,t2=3---->t1=4,t2=5---->t1=9,t2=7
        System.out.println("和为:"+sum.get());
        //求最值
        Optional<Integer> reduce = list.stream().reduce((t1, t2) -> t1 > t2?t1:t2);
        System.out.println("最大值为:"+reduce.get());
        //求积
        Optional<Integer> reduce2 = list.stream().reduce((t1, t2) -> t1 * t2);
        System.out.println("积为:"+reduce2.get());
    }
}

5.5.7 findFirst(查找第一个元素)

需求:查找出集合中年龄大于6且小于二十的第一条数据

public class Test1 {
    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'));

        //查找符合条件的第一个数据
        Person first = personList.stream().filter(item -> item.getAge() >= 6 && item.getAge() <= 20).findFirst().get();
        System.out.println(first);

    }
}

5.5.8 提取、组合

Stream流也可以进行合并、去重、限制、跳过等操作。

public class Test03 {
    public static void main(String[] args) {

        String[] arr = {"a","b","c","d"};
        String[] arr2 = {"c","d","e","f"};
        Stream<String> stream1 = Stream.of(arr);
        Stream<String> stream2 = Stream.of(arr2);
        //concat:合并两个流 distinct: 去重
        List<String> list = Stream.concat(stream1, stream2).distinct().collect(Collectors.toList());
        System.out.println("合并后的:"+list);
        //limit: 获取流中前n个数据
        List<Integer> collect = Stream.iterate(1, t -> t + 1).limit(5).collect(Collectors.toList());
        System.out.println("limit获取前五个元素:"+collect);
        //skip: 跳过前n个数据
        List<Integer> collect2 = Stream.iterate(1, t -> t + 1).skip(3).limit(5).collect(Collectors.toList());
        System.out.println("skip跳过前三个元素:"+collect2);
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值