专栏首页Java探索之路工作后, 你一定不能错过技术之JDK1.8的新特性

工作后, 你一定不能错过技术之JDK1.8的新特性

在现在的企业级开发中, 随着新技术的迭代, 越来越多的公司开始使用Java8的新特性去简化开发, 因此Java8非常值得我们学习. 在学习Java8的时候, 我们需要了解一下Java8都有哪些主要的新特性

  • 函数式接口
  • Lambda表达式
  • Stream集合的流式编程
  • 新时间日期API等

而在企业开发中, 主要用到的便是Lambda表达式Stream流 , 而在下面,我们便主要的去学习这两方面的知识

Lambda表达式

Lambda表达式本质上是一个匿名函数, 可以简单理解为一个可以将代码像数据一样传递的代码 特点: 简洁, 灵活

基本语法

在写lambda表达式时, 只需关注函数和方法体即可

  • 参数: 写在小括号中
  • 方法体: 写在大括号内
  • ->: lambda表达式运算符, 分隔参数和方法体

注意:

  • lambda主要用于简化函数式接口, 而函数式接口在Java中是指: 有且仅有一个抽象方法的接口
  • 只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导

举例:

定义了三个接口, 利用lambda去实现相关功能

/**
 * Author TimePause
 * Create  2020-05-03 22:03
 */
public interface SingleReturnMultipleParameter {
    int test(int a,int b);
}

/**
 * Author TimePause
 * Create  2020-05-03 22:04
 */
public interface NoneReturnParmarter {
    void test();
}

/**
 * Author TimePause
 * Create  2020-05-03 22:26
 */
public interface SingleReturnSingleParameter {
    int test(int x);
}


/**
 * lambda表达式: 对接口进行简洁的实现
 *
 * @author TimePause
 * @create 2020-05-03 22:05
 */
public class TestLambda {
    public static void main(String[] args) {
        //测试lambda无返回值无参
        NoneReturnParmarter lambda1= ()-> {
            System.out.println("lambda-NoneReturnParmarter");
        };
        lambda1.test();
        //测试lambda单个返回值多个参数
        SingleReturnMultipleParameter lambda2=(int a,int b)->{
            return a + b;
        };
        System.out.println(lambda2.test(1, 2));
        //测试lambda单个参数单个返回值
        SingleReturnSingleParameter lambda3=(int x)->{
            return x*x;
        };
        System.out.println(lambda3.test(3));

        //测试lambda单个/多个参数省略参数类型
        SingleReturnMultipleParameter lambda5=(a,b)->{
            return a + b;
        };
        System.out.println(lambda5.test(1, 2));

        //测试lambda单个参数省略小括号(两个无法省略)
        SingleReturnSingleParameter lambda6= x->{return x*x; };

        /**
         *  注意:
         *  1.如果方法体中代码只有一行,那么大括号可以省略
         *  2.如果唯一的一行代码是返回语句,那么在大括号省略的同时, 返回值也必须省略
         */
        NoneReturnParmarter lambda7= ()-> System.out.println("lambda-NoneReturnParmarter");
        SingleReturnSingleParameter lambda8= x-> x*x;

    }
}

lambda表达式的函数引用

  • 将一个接口的实现,引用到一个已存在的方法上
  • 注意: 要引用的方法的参数和返回值要和接口中定义的一致

举例:

使用上面实现过的接口来实现引用

/**
 * Author TimePause
 * Create  2020-05-03 22:03
 */
public interface SingleReturnMultipleParameter {
    int test(int a,int b);
}

/**
 * 测试lambda表达式的引用
 *
 * @author TimePause
 * @create 2020-05-04 22:11
 */
public class TestLambda2 {
    public static void main(String[] args) {
        //测试lambda表达式的引用: 类名::方法名
        SingleReturnMultipleParameter lambda=TestLambda2::getAbs;
        lambda.test(2, 3);
        //相当于下面的方式
        SingleReturnMultipleParameter lambda1 = (a, b) -> TestLambda2.getAbs(a, b);
        System.out.println(lambda1.test(2, 3));
        //测试对非静态方法的引用
        NoneReturnParmarter lambda2=new TestLambda2()::show;
        lambda2.test();
    }

    /**
     * 引用注意:
     * 1.把私有修饰符去掉以后可在其他类中通过 类名::方法名引用其他方法
     * 2.如果是静态方法引用方式为: 类名::方法名
     *   如果是非静态方法引用方式为: 对象::方法名
     */
     static int getAbs(int a,int b){
        return a>b?a-b:b-a;
    }

    public void show(){
        System.out.println("如果是非静态方法引用方式为: 对象::方法名");
    }
}

lambda表达式对构造方法的引用

lambda在引用时,会根据接口类型不同自动适配是带参还是无参的构造方法!!!

/**
 * 测试lambda表达式对构造方法的引用
 *
 * @author TimePause
 * @create 2020-05-04 22:38
 */
public class TestLambda3 {
    public static void main(String[] args) {
        //lambda在引用时,会根据接口类型不同自动适配是带参还是无参的构造方法!!!
        PersonMake lambda1=Person::new;
        PersonMake2 lambda2 = Person::new;
    }
}
//定义一个实体类两个接口, 分别测试lambda对带参和无参方法的引用
class Person{
    String name;
    int age;

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

interface PersonMake{
    Person getPerson();
}

interface  PersonMake2{
    Person getPerson(String name,int age);
}

lambda表达式对get(),set()方法的引用

引用时需要根据接口进行适配

public class TestLambda3 {
    public static void main(String[] args) {
        //对get方法的引用
        getName lambda4=Person::getName;
        //对set方法的引用
        setName lambda5=new Person()::setName;
    }
}
class Person{
    String name;
    int age;
    
    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setAge(int age) {
        this.age = age;
    }
}


interface  getName{
    String get(Person p);
}

interface setName{
    void set(String name);
}

lambda简单应用1

/**
 * 测试lambda表达式实现数组的降序排序
 *
 * @author TimePause
 * @create 2020-05-05 9:24
 */
public class TestLambda4 {
    public static void main(String[] args) {
        Integer arr[] = {1, 9, 8, 3, 5, 6};
        //ctrl+f12=>查看当前类下所有方法
        Arrays.sort(arr);//这是正序
        System.out.println(Arrays.toString(arr));
        Arrays.sort(arr,(x,y)-> y-x);//这是倒序
        System.out.println(Arrays.toString(arr));
    }
}

简单应用2

将上面的TestLambda3 代码进行修改,可以测试lambda对对象数组的某个属性进行排序的情况

public class TestLambda3 {
    public static void main(String[] args) {
        Person[] persons=new Person[5];
        persons[0] = new Person("小1", 33);
        persons[1] = new Person("小2", 21);
        persons[2] = new Person("小3", 25);
        persons[3] = new Person("小4", 28);
        persons[4] = new Person("小5", 19);
        //利用lambda对其按照年龄进行正序排序
        Arrays.sort(persons,(p1,p2)->p1.getAge()-p2.getAge());
        for (int i = 0; i < persons.length; i++) {
            System.out.println(persons[i]);
        }
    }
}
//定义一个实体类两个接口, 分别测试lambda对带参和无参方法的引用
class Person{
    String name;
    int age;

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

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

Stream(集合的流式编程)

在对集合中的元素进行操作的时候, 有时候可能会用到其他结果, 在这个过程中使用集合的流式编程可以简化代码量, 将数据源中的数据读取到一个流中, 可以对这个流进行操作(删除, 过滤, 映射…)

介绍

Stream流是对集合操作的增强, 它不是一种数据结构,也不负责存储. 更像一个迭代器,且单向遍历不可循环

实现步骤

  1. 获取数据源, 读取到流中
  2. 对流中的数据进行操作(中间操作)
  3. 对流中的数据进行整合处理(最终操作)

注意:

  • 几乎所有中间操作最终操作的参数和方法都是函数式接口,
  • 因此使用集合的流式编程进行简化处理的前提是熟练使用lambda表达式

数据源的获取

数据源就是数据的来源, 从数据源中读取到流中 需要注意的是对流中数据的操作(删除, 映射, 过滤…)是不会影响数据源的数据的

/**
 * 测试Stream流式编程获取数据源
 *
 * @author TimePause
 * @create 2020-05-05 11:34
 */
public class TestStream {
    public static void main(String[] args) {
        List<Object> list = new ArrayList<>();
        /**
         * 数据源的获取
         * 1.stream获取的数据源是串行的
         * 2.parallelStream获取的数据源是并行的, 且该方法封装了多线程对数据的操作, 效率更高
         */
        list.stream();
        list.parallelStream();

        //Stream获取数组元素
        int[] arr={1,9,8,0,2,3};
        IntStream stream = Arrays.stream(arr);
    }
}

最终操作

最终操作指的是将流中的数据整合在一起, 放入一个集合, 也可以直接对流中的数据进行遍历统计, 提取出我们想要的信息 注意: 在使用最终操作后会关闭这个流并销毁流中的数据, 如果再次使用这个已关闭的流则会出现异常

/**
 * 测试最终操作
 *
 * @author TimePause
 * @create 2020-05-05 12:09
 */
public class TestStream2 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<Integer>();
        Collections.addAll(list, 5, 10, 15, 20, 25);
        /**
         * 1.collect:
         * 通过在collect()方法内调用Collectors的其他方法,将流中的数据转化成其他类型的数据
         */
        //将最终结果转成list
        List<Integer> collect = list.stream().collect(Collectors.toList());
        System.out.println(collect);
        //将最终结果转成set
        Set<Integer> set = list.stream().collect(Collectors.toSet());
        System.out.println(set);
        //将最终结果转成map
        Map<Integer, Integer> map = list.stream().collect(Collectors.toMap(e -> e / 5, e -> e));
        System.out.println(map);

        /**
         * 2.reduce:将流中的所有元素带入这个方法中进行运算
         * 最终的运算结果是一个optional类型的数据,需要调用get()方法获取其中的数据
         */
        int res = list.stream().reduce((e1, e2) -> e1 + e2).get();
        System.out.println(res);

        /**
         * 3.count:统计流中的数据的个数
         */
        long count = list.stream().count();
        System.out.println(count);

        /**
         * 4.foreach:遍历流中的元素
         */
        list.stream().forEach(System.out::println);

        /**
         * 5.max&min: 获取流中的最大值和最小值
         */
        Integer max = list.stream().max(Integer::compareTo).get();
        System.out.println("集合中的最大值是: "+max);
        Integer min = list.stream().min(Integer::compareTo).get();
        System.out.println("集合中的最小值是: "+min);

        /**
         * 6.allMatch & anyMatch & noneMatch=>matching
         * allMatch: 只有流中所有元素都满足匹配规则,才返回true
         * anyMatch: 只要有任意一个元素满足匹配规则,就返回true
         * noneMatch: 只有流中所有的元素都不满足匹配规则,才返回true
         */
        boolean allMatch = list.stream().allMatch(e -> e > 20);
        System.out.println(allMatch);
        boolean anyMatch = list.stream().anyMatch(e -> e > 20);
        System.out.println(anyMatch);
        boolean noneMatch = list.stream().noneMatch(e -> e %5 !=0);
        System.out.println(noneMatch);

        /**
         * 7.find: 查找流中的第一个元素
         * findFirst()和findAny()在绝大多数情况下作用是一样的, 只有在多线程的环境下才可能不同
         */
        Integer findFirst = list.parallelStream().findFirst().get();
        Integer findAny = list.parallelStream().findAny().get();
        System.out.println(findFirst +"---"+findAny);

		/**
         * 注意事项:在使用最终操作后会关闭这个流并销毁流中的数据, 如果再次使用这个已关闭的流则会出现异常
         * 异常打印如下:
         * Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
         * 	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
         * 	at java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:464)
         */
        Stream<Integer> stream = list.stream();
        long count1 = stream.count();
        System.out.println(count1);
        Integer integer = stream.findFirst().get();
        System.out.println(integer);
    }
}

中间操作

中间操作指的是对流中的数据做各式各样的处理, 中间操作可以是连续的操作, 每次操作都返回一个Stream对象, 直到最终操作的执行

/**
 * 测试Stream的中间操作(对数组,集合进行操作)
 *
 * @author TimePause
 * @create 2020-05-05 15:45
 */
public class TestStream3 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "hello", "world", "hello", "TimePause", "csdn");
        /**
         * 1.filter:过滤流中符合条件的元素
         */
        list.stream().filter(e->e.length()>5).forEach(System.out::println);

        /**
         * 2.distinct: 去除集合中重复的元素
         * 这个方法没有参数, 去重规则与hashset相同(会比较hashcode,如果hashcode相同会比较equals,equals相同则认为相同则去重)
         */
        list.stream().distinct().forEach(System.out::println);

        /**
         * 3.sorted: 排序
         * sorted():默认是按照流中的元素对应的类,实现的comparable接口中的方法进行排序
         *          也可以在()内将流中的数据按照指定的规则进行排序
         */
        list.stream().sorted().forEach(System.out::println);
        list.stream().sorted((e1,e2)->e1.length()-e2.length()).forEach(System.out::println);

        /**
         * 4.skip & limit (这两个方法一般会联合使用)
         * skip(): 跳过指定个数的元素
         * limit():截取指定个数的元素
         */
        list.stream().skip(2).forEach(System.out::println);
        list.stream().limit(2).forEach(System.out::println);
        //如何获取 hello TimePause?
        list.stream().skip(2).limit(2).forEach(System.out::println);

        /**
         * 5. map & flatMap (重要!!!)
         * map: 对流中的数据进行映射,用新的数据替换旧的数据
         * flatMap: 也是元素的映射,不过是扁平化的映射, 将容器中所有元素取出放到集合中
         */
        list.stream().map(e->e+".txt").forEach(System.out::println);

        String[] strs = {"hello world", "hello csdn", "hello TimePause", "hello csdn"};
        //数组也可以使用Stream
        Arrays.stream(strs).map(String::toCharArray).forEach(e-> System.out.println(Arrays.toString(e)));
        Arrays.stream(strs).map(e->e.split(" ")).flatMap(Arrays::stream).forEach(System.out::println); //strs->map(arrays)->flatMap(elements)

    }
}
/**
 * 测试Stream中间操作之maptoint (对对象组成的集合进行操作)
 *
 * @author TimePause
 * @create 2020-05-05 20:58
 */
public class TestStream4 {
    public static void main(String[] args) {
        ArrayList<Persons> person = new ArrayList<>();
        Collections.addAll(person,new Persons("小a",14),new Persons("小b",12),new Persons("小c",33));

        //Integer sum = person.stream().map(e -> e.getAge()).reduce((e1, e2) -> e1 + e2).get();
        //System.out.println(sum);
        /**
         * maptoInt: 将流中的数据转成int,此时这个方法的返回值不再是Stream
         *           此时这个方法的返回值不再是Stream而是IntStream
         */
        double average = person.stream().mapToInt(Persons::getAge).average().getAsDouble();//获取平均年龄
        System.out.println(average);
    }
}
class Persons{
    String name;
    int age;

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

    public int getAge() {
        return age;
    }
}

综合案例

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 综合练习
 *
 * @author TimePause
 * @create 2020-05-05 21:27
 */
public class TestStream5 {
    public static void main(String[] args) {
        ArrayList<Student> studentList = new ArrayList<>();
        Collections.addAll(studentList,
                new Student("小1",59),
                new Student("小2",99),
                new Student("小3",87),
                new Student("小4",68),
                new Student("小5",71)
        );

        //1.求出所有及格的学生
        System.out.println("---------------1.求出所有及格的学生--------------");
        studentList.
                stream().
                filter(e->e.getScore()>60).
                forEach(System.out::println);

        //2.求出所有及格的学生的姓名
        System.out.println("---------------2.求出所有及格的学生的姓名--------------");
        studentList.
                stream().
                filter(e->e.getScore()>60).
                map(Student::getName).
                forEach(System.out::println);

        //3.求出所有学生的平均成绩
        System.out.println("---------------3.求出所有学生的平均成绩--------------");
        double averageScore = studentList.
                stream().
                mapToInt(Student::getScore).
                average().getAsDouble();
        System.out.println(averageScore);

        //4.求班级前三名(做成集合)???
        System.out.println("---------------4.求班级前三名(做成集合)--------------");
        List<Student> collect = studentList.
                stream().
                sorted((e1, e2) -> e2.getScore() - e1.getScore()).
                limit(3).
                collect(Collectors.toList());
        collect.forEach(System.out::println);

        //5.求班级的3-10名(做成集合)
        System.out.println("---------------5.求班级的3-10名(做成集合)--------------");
        List<Student> collect2 = studentList.
                stream().
                sorted((e1, e2) -> e2.getScore() - e1.getScore()).
                limit(10).
                skip(2).
                collect(Collectors.toList());
        collect2.forEach(System.out::println);

        //6.求所有不及格学生的平均成绩
        System.out.println("---------------6.求所有不及格学生的平均成绩--------------");
        double averageScore2 = studentList.
                stream().
                filter(e -> e.getScore() < 60).
                mapToInt(e -> e.getScore()).
                average().
                getAsDouble();
        System.out.println(averageScore2);

        //7.将及格的学生, 按照成绩降序输出所有信息
        System.out.println("---------------7.将及格的学生, 按照成绩降序输出所有信息--------------");
        studentList.
                stream().
                filter(e->e.getScore()>60).
                sorted((e1,e2)->e2.getScore()-e1.getScore()).
                forEach(System.out::println);


        //8.班级学生的总分=>从这里可以看出很多时候e->e.getScore()可以替换Student::getScore,都是对get()方法的引用
        System.out.println("---------------8.班级学生的总分--------------");
        int res = studentList.stream().mapToInt(Student::getScore).reduce((e1, e2) -> e1 + e2).getAsInt();
        int res2 = studentList.stream().mapToInt(e->e.getScore()).reduce((e1, e2) -> e1 + e2).getAsInt();
        int res3 = studentList.stream().mapToInt(e -> e.getScore()).sum();
        System.out.println(res);
        System.out.println(res2);
        System.out.println(res3);
    }
}

class Student{
    private String name;
    private int score;

    public Student(String name,int score ){
        this.name=name;
        this.score = score;
    }

    public String getName(){
        return name;
    }

    public int getScore(){
        return score;
    }

    @Override
    public String toString() {
        return String.format("姓名: %s, 成绩: %d", name, score);
    }
}

聪明的人能够看到本文配套学习视频哦: https://www.bilibili.com/video/BV1N7411v796?p=1

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 设计模式之结构型模式

    动态代理与静态代理的比较 抽象角色中(接口)声明的所以方法都被转移到调用处理器一个集中的方法中处理 , 我们可以更加灵活和统一的处理众多的方法。

    时间静止不是简史
  • 设计模式之行为型模式

    将一个请求封装成一个对象 ,从而使我们可用不同请求对客户进行参数化 :对请求排队或记录请求日志 ,以及支持可撤销的操作 .也叫: 动作Action模式 ,事务t...

    时间静止不是简史
  • Spring框架

    注:当在service的一个方法中同时使用了JdbcTemplate和HibernateTemplate时,就要使用HibernateTransactionMa...

    时间静止不是简史
  • Java8学习之Stream(流)

    本文讲述.stream()的内容,需要一些Lambda表达式的基础,之前也推送过关于Lambda表达式和Stream的相关内容,就看哪盘菜味道更好!

    用户5927304
  • 解决PKIX问题:unable to find valid certification path to requested target

    版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。

    奋飛
  • 单例设计模式

    喜欢天文的pony站长
  • 【Java学习笔记之二十二】解析接口在Java继承中的用法及实例分析

    一、定义 Java接口(Interface),是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不...

    Angel_Kitty
  • 线程的三个同步器

    日常中会有开启多个线程去并发执行任务,而主线程要等所有子线程执行完之后才能运行的需求。之前我们是使用Thread.join方法来实现的,过程如下:

    晚上没宵夜
  • JAVA之线程状态(二)

    PS:通过API,演示前面说的几种状态,通过图和代码让大家对这6种状态了解透彻,线程状态的知识点就讲解到这里。下次咱们继续说说线程,一起说说线程终止。

    IT故事会
  • Spring Boot 2.X(十):自定义注册 Servlet、Filter、Listener

    在 Spring Boot 中已经移除了 web.xml 文件,如果需要注册添加 Servlet、Filter、Listener 为 Spring Bean,在...

    朝雾轻寒

扫码关注云+社区

领取腾讯云代金券