【进阶】Java8新特性的理解与应用

【进阶】Java8新特性的理解与应用

前言

Java 8是Java的一个重大版本,是目前企业中使用最广泛的一个版本。

它支持函数式编程,新的Stream API 、新的日期 API等一系列新特性。

掌握Java8的新特性已经是java程序员的标配,掌握了它,就可以看懂公司里的代码、高效率地处理大量集合数据以及消灭”嵌套地狱”等等。

一、Lambda表达式

Lambda表达式是java8最重要的新特性之一,与Stream API一起成为JDK1.8最主要的更新内容。

Lambda是一个匿名函数(表达式),可以将Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。

这样可以写出更简洁、更灵活的代码。同时作为一种更紧凑的代码风格,使java的语言表达能力得到了提升。

9.1基础概念

  • 首先,lambda表达式需要函数式接口的支持,lambda表达式的实现是基于函数式接口的。
  • lambda表达式的底层思维还是执行方法(函数),但lambda表达式会使得代码更简洁,利于程序员编写。
  • Java8中引入了一个新的操作符”->”,该操作符成为箭头操作符或者lambda操作符。该操作符将lambda表达式分为了左侧和右侧两部分。
  • 操作符左侧:lambda表达式所需的参数列表,具体就是lambda表达式中接口抽象方法的参数列表;
  • 操作符右侧:lambda表达式所需执行的功能,即lambda体,也就是接口中抽象方法具体要实现的功能。

9.2语法格式

9.2.1格式一:抽象方法无参数、无返回值
 /**
     *语法格式一:抽象方法无参数、无返回值
     * */
    @Test
    public void test_1(){
        //Runnable接口无参数、无返回值,无参数直接使用()
        Runnable  r = () -> System.out.println("Hello,Lambda!");
        r.run();
    }
9.2.2格式二:抽象方法有1个参数,无返回值
  /**
     *语法格式二:抽象方法有1个参数,且无返回值
     * */
    @Test
    public void test_2(){
        //Consumer接口是JDK中一个有参、无返回的接口,作用是消费接口传进来的泛型参数
        Consumer con = (t) -> System.out.println(t);
        //accept()是该接口的抽象方法
        con.accept("Hello Lambda!");
    }

注:如该抽象方法的参数只有1个,则”->”的左侧可以省略()不写。

9.2.3格式三:抽象方法中有多个参数、有返回值,且lambda体中有多条语句
    /**
     * 语法格式三:抽象方法中有多个参数、有返回值,且lambda体中有多条语句
     * */
    @Test
    public void test_3(){
        //如果lambda体中有多条语句,则必须将语句写在{};中
        Comparator com = (x,y) -> {
            System.out.println("函数式接口");
            return Integer.compare(x,y);
        };
    }

注:如该lambda体中只有一条语句,则{};和return都可以省略不写。

9.2.4lambda表达式中参数列表的数据类型可以省略
    /**
     *语法格式四:lambda表达式中参数列表的数据类型可以省略,JVM可以根绝上下文进行推断,这个过程称为"类型推断"。
     *          本质上来说,由于lambda表达式基于函数式接口来实现,函数式接口中的抽象方法(T t)已经指定了泛型的数据类型。
     **/
    @Test
    public void test_4(){
        //参数列表中的Integer可以省略不写
        Comparator c = (Integer x,Integer y) -> Integer.compare(x,y);
    }

9.3lambda表达式的应用

9.3.1需求1
调用Collections.sort()方法,定制化排序比较两个员工对象信息(第一比较年龄,年龄相同比较姓名),参数传递方式使用lambda表达式的形式。
    //定义员工信息list
    List user_list = Arrays.asList(
            new User("张三",21,"xbzhu@163.com"),
            new User("李四",35,"ewrtrv@163.com")
    );
    @Test
    public void test_1(){
        //Collections接口,使用lambda表达式
        Collections.sort(user_list,(u1,u2) -> {
            //lambda体中有多条语句
            if (u1.getAge() == u2.getAge()){
                return u1.getName().compareTo(u2.getName());
            }else {
                return -Integer.compare(u1.getAge(), u2.getAge());
            }
        });
        for (User u : user_list){
            System.out.println(u);
        }
    }
9.3.2需求2
 a.声明一个函数式接口,同时在该接口中声明一个抽象方法 String getValue(String str);
 b.声明一个类TestLambda_3,类中编写成员方法test_2,使用a中定义的接口作为该方法的参数,将一个字符串"lambda"转换为大写,并作为方法的返回值;
 c.再将该字符串的第2和第4个索引位置的的字符进行字串截取。
 /**
     * 该成员方法的形参1表示的是需要被操作的字符串,形参2表示的是接口中的操作实现。
     * */
    public String transform(String str, MyPractice mp){
        return mp.getValue(str);
    }

    @Test
    public void test_2(){
        String s = transform("lambda",(str) -> str.toUpperCase());
        System.out.println(s);
        //subString()方法截取规则:从数组下标的方式进行计算,含头不含尾。
        String ss = transform("lambda",(str -> str.substring(2,5)));
        System.out.println(ss);
    }
9.3.3需求3
a.声明一个带2个泛型的函数式接口,其中泛型类型为且T为参数,R为返回值,同时在该接口中声明对应的抽象方法;
b.在类TestLambda_3中声明一个成员方法calculate()并使用a中的接口作为参数,输出员工信息。
    /**
     * 该成员方法中形参1表示的是需要被操作的字符串,形参2表示的是接口中的操作实现。
     * */
    public  List calculate(MyPractice_2> mp){
        mp.calculateValues();
        return user_list;
    }

    @Test
    public List test_3(){
        List list = calculate(() -> {
            System.out.println(user_list);
            return null;
        });
        return list;
    }

二、函数式编程

在java中(尤其从java8开始),函数式接口的应用是函数式编程的一个典型实现。

基于以上对lambda表达式的认识,我们可以清楚地知道:lambda表达式的实现需要函数式接口的支持。

2.1函数式接口

函数式接口指的是:接口中只有一个抽象方法的接口,称之为函数式接口。并且可以使用@FunctionnalInterface注解修饰,以此来判断该接口是否是函数式接口。

在Java8以后,函数式接口中允许存在普通方法(即非抽象方法),使用default进行修饰。

2.2内置4大核心函数式接口

  1. Cosumer消费型接口;
//抽象方法
void accept(T t);
  1. Supploer< T> 供给型接口;
//抽象方法
T get();
  1. Function< T> 函数型接口;
//抽象方法
R apply(T t);
  1. Predicate< T>断言式接口;
//抽象方法
boolean test(T t);

三、Stream流 API

Java8最为主要的更新内容是lambda表达式和Stream流API,关于lambda表达式在上面已经介绍过了,下面就来看看今天的主角——Stream流 API(java.util.stream.*)。

3.1基本概念

  • Stream API是java8中处理集合的关键抽象概念,它可以对指定的集合进行操作,如执行非常复杂的查找、过滤和映射数据等操作;
  • 使用Stream API对集合数据进行操作,类似于使用SQL执行数据库查询的操作;
  • Stream API在对数据源(集合或数组)进行一系列流水线式的操作后,最终会产生一个新的流。

注意

  1. Stream本身不会存储元素;
  2. Stream不会改变源对象,相反,Stream流执行完后会返回一个有结果的新Stream;
  3. Stream流的执行具有延迟性,只有当执行流的终止操作时(或者需要某些结果时),Stream才会执行。

简而言之,Stream API提供了一种高效且易于使用的处理数据的方式。

3.2实现步骤

Stream流的操作可分为3个步骤:创建Stream、中间操作以及终止操作(结果)。

3.2.1步骤一:创建Stream
 //可以通过Collection系列集合提供的stream(),或者parallelStream()
         List list_1 = new ArrayList<>();
         Stream stream_1 = list_1.stream();
//可以通过of()创建Stream的
         Stream stream_2 = Stream.of("aa","bb","cc");
//创建无限流
         Stream stream_3 = Stream.iterate(3,(x) -> x+2);
         stream_3.limit(10).forEach(System.out::println);
3.2.2步骤二:中间操作

中间操作主要包括:筛选与切片、映射,查找和排序等。

  1. 筛选与切片、映射
 /**
     * 筛选与切片
     * filter:接收Lambda,从流中排除某些元素;
     * map:接收Lambda,将元素转换为其它形式或者提取数据源的具体信息;(接收一个函数作为参数,该函数会应用到每个元素上,并将其映射成一个新的元素)
     * limit(n):截断流,使其元素不超过指定数量,即只取前n个元素;
     * skip(n):跳过元素,返回n个后的元素;
     * distinct:通过流生成元素的hashCode()和equals()去除重复元素。
     * */
    @Test
    public void test_1(){
         List a = Arrays.asList(1,2,3,4,5,6,7,8,9,9,9);
                //创建Stream
                a.stream()
                //中间操作
                .filter(i -> i%2 ==0 || i%3 ==0)
                .limit(7)
                .map(i -> i * i)
                .distinct()
                //终止操作
                .forEach(System.out::println);
    }

打开IDEA的调试模式,并点击横排图标最右侧的”Trace Current Stream Chain”就可追踪到Stream流的一些中间操作,具体如图3-1所示:

【进阶】Java8新特性的理解与应用

图3-1

  1. 查找
 /**
     * 查找
     * findFirst:查找流中的第一个元素
     * findAny:查找流中的任意一个元素
     * count:返回流中元素的总个数
     * */
    @Test
    public void testMatch(){

        //比较过后获取流中第一个元素,并放入Optional容器中
        Optional op = user_list.stream()
                .sorted((e1,e2) -> -Double.compare(e1.getAge(), e2.getAge()))
                .findFirst();
        System.out.println(op.get());

        //从流中取出任意一个符合条件的元素,并放入Optional容器中
        Optional op_2 = user_list.parallelStream()
                .filter((e) -> e.getStatus().equals(User.Status.STATUS_0))
                .findAny();
        System.out.println(op_2.get());

        //获取流中元素的个数
        Long count = user_list.stream()
                //filter加以条件限制
                .filter((e) -> e.getAge() > 30)
                .count();
        System.out.println(count);

        //获取最小的年龄(而不是最小年龄员工的信息)
        Optional op_3 = user_list.stream()
                .map(User::getAge)
                .min(Integer::compare);
        System.out.println(op_3.get());
    }

对于获取源数据(如集合)中的具体某个元素,可以使用map()将所需信息提取出来,然后再进行比较操作。过程分析如下图3-2所示:

【进阶】Java8新特性的理解与应用

图3-2

  1. 排序
  /**
     * 排序
     * 1、sorted:自然排序(Comparable);
     * 2、sorted(Comparator com):定制排序。
     * */
    @Test
    public void test_1(){
        List list = Arrays.asList(564,457,54,43,1,4,65);
        list.stream()
                .sorted()
                .forEach(System.out::println);

        user_list.stream()
                 .sorted((e1,e2) -> {
                     //年龄是否相同
                     if (e1.getAge().equals(e2.getAge())){
                         //年龄相同按照姓名比
                         return e1.getName().compareTo(e2.getName());
                     }else
                         return e1.getAge().compareTo(e2.getAge());
                 })
                 .forEach(System.out::println);
    }

四、时间日期 API

Java8中更新了时间日期相关的API,主要包括时间日期转换、时间校正器等。

4.1时间日期转换

在实际开发中的时间日期转换主要包括Date类型与String的互相转换、Long类型时间转换为String、Long类型时间转换为Date。

4.1.1Date与String的互转
  • DateTimeFormatter类
 /**
     * 时间格式转化:
     * 1、获取当前Date类型时间;
     * 2、将该时间转化为String类型;
     * 3、返回(输出)String类型的时间。
     * */
    @Test
    public void test_1(){
        //获取当前时间
        LocalDateTime ldt = LocalDateTime.now();
        //指定需要转化的格式
        DateTimeFormatter simpleDateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        //将当前时间按照指定格式,转化为String类型输出
        String time = simpleDateFormat.format(ldt);
        System.out.println(time);
    }
  • @JsonFormat注解
  /**
     * 该注解将String类型的数据按照指定格式返回,但其数据类型仍然还是String
     * */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private String activityStartTime;
4.1.2Long转换为String(Date)
  • getTime()方法、Calendar类、SimpleDateFormat类
 /**
     * 时间格式转化:
     * 1、获取long类型的时间数(毫秒数);
     * 2、将该时间转化为String类型;
     * 3、返回(输出)String类型的时间。
     * */
    @Test
    public void test_2(){
        DateTest dateTest = new DateTest();
        //getTime()方法获取long类型时间数
        Long ssTime = dateTest.getTestTime().getTime();
        //声明一个Calendar类的通用对象
        Calendar calendar = Calendar.getInstance();
        //将long类型时间Set为Date类型
        calendar.setTimeInMillis(ssTime);
        System.out.println(calendar);
        //指定需要转化的格式
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String time = simpleDateFormat.format(calendar.getTime());
        System.out.println(time);
    }

Original: https://www.cnblogs.com/Apluemxa/p/16354747.html
Author: Apluemxa
Title: 【进阶】Java8新特性的理解与应用

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/574797/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • easyUI 自定义排序datagrid

    @author YHC 如果默认的排序行为不满足你的需求,你可以自定义datagrid排序行为. 最基础的用户可以定义一个排序函数,函数名是s orter 在列上,这个函数将接受两…

    Java 2023年5月29日
    055
  • 动态规划—摘花生

    Hello Kitty想摘点花生送给她喜欢的米老鼠。 她来到一片有网格状道路的矩形花生地(如下图),从西北角进去,东南角出来。 地里每个道路的交叉点上都有种着一株花生苗,上面有若干…

    Java 2023年6月7日
    081
  • Linux下搭建maven(maven3.6+nexus3.2)私服

    准备maven和nexus安装包,nexus安装包好像要FQ,不然下载不到! 链接:https://pan.baidu.com/s/1bVMadGoTAK9pSLW6yBNOCg提…

    Java 2023年6月8日
    082
  • Nginx作反向代理时超时重试配置

    nginx重置,nginx超时 这里只讨论作反向代理时,当上游服务发生如接口超时、返回指定状态码等状况时而导致nginx超时重试。 这里使用的nginx版本为 1.16.1,可通过…

    Java 2023年6月8日
    082
  • IDEA中提示配置jdk1.8

    问题描述:运行Java Web项目时,IDEA中提示:Warning:java: 源值1.5已过时, 将在未来所有发行版中删除 解决方法:1. 打开【File】—【Project …

    Java 2023年5月30日
    053
  • EasyExcel配置步骤

    1.介绍 EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目参考https://blog.csdn.net/u013044713/article/deta…

    Java 2023年6月9日
    055
  • 【干货】MySQL底层架构设计,你了解多少?

    很多开发同学对SQL优化如数家珍,却对MySQL架构一知半解。岂不是只见树叶,不见森林,终将陷入细节中不能自拔。 今天就一块学习MySQL分层架构,深入了解MySQL底层实现原理,…

    Java 2023年6月8日
    074
  • Ubuntu 制作 系统的启动盘

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Java 2023年6月8日
    069
  • 栈和队列

    写在前面 栈和队列,也属于线性表,因为它们也都用于存储逻辑关系为 “一对一” 的数据。使用栈结构存储数据,讲究 先进后出,即最先进栈的数据,最后出栈;使用队…

    Java 2023年6月5日
    054
  • Java中对象池的本质是什么?(实战分析版)

    对象池顾名思义就是存放对象的池,与我们常听到的线程池、数据库连接池、http连接池等一样,都是典型的池化设计思想。 对象池的优点就是可以集中管理池中对象,减少频繁创建和销毁长期使用…

    Java 2023年5月29日
    078
  • 使用POI 技术 的SetColumnWidth 精确控制列宽不能成功的解决办法(java)

    在使用NPOI技术开发自动操作EXCEL软件时遇到不能精确设置列宽的问题。 ISheet sheet1 = hssfworkbook.CreateSheet(“Shee…

    Java 2023年5月29日
    057
  • oracle日期转换错误:ORA-01830

    to_date函数错误: select to_date(‘2019-01-23′,’yyyy-MM’) from dual 中文含义…

    Java 2023年6月5日
    067
  • MYSQL安装教程 详细版

    通过这个路径可以直接下载到mysql5.7的安装包 添加一下path路径,这样我们能从任何位置打开mysql 添加path路径 配置my.ini ,如果没有这个文件就新建一个 修改…

    Java 2023年6月9日
    086
  • 选择结构(Java)

    if选择结构 if语法 if(&#x5E03;&#x5C14;&#x8868;&#x8FBE;&#x5F0F;){&#x5982;&…

    Java 2023年6月9日
    058
  • zookeeper_overview

    概述 zk 是一个开源的,分布式协调服务,它的目的就是为了服务于分布式应用。zk 允许分布式应用通过 zk 的节点进行相互协调,常见的有配置同步、分布式锁、微服务注册与发现等等。 …

    Java 2023年6月8日
    064
  • 使用jquery实现文本框输入特效:文字逐个显示逐个消失反复循环

    前两天看到某个网站上的输入框有个小特效:文字逐个显示,并且到字符串最大长度后,逐个消失,然后重新循环显示消失,循环显示字符串数组。我对这个小特效有点好奇,于是今天自己尝试用jque…

    Java 2023年6月6日
    065
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球