【进阶】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<String> con = (t) -> System.out.println(t);
//accept()是该接口的抽象方法
con.accept("Hello Lambda!");
}

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

9.2.3格式三:抽象方法中有多个参数、有返回值,且lambda体中有多条语句
    /**
* 语法格式三:抽象方法中有多个参数、有返回值,且lambda体中有多条语句
* */
@Test
public void test_3(){
//如果lambda体中有多条语句,则必须将语句写在{};中
Comparator<Integer> 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<Integer> c = (Integer x,Integer y) -> Integer.compare(x,y);
}

9.3lambda表达式的应用

9.3.1需求1
调用Collections.sort()方法,定制化排序比较两个员工对象信息(第一比较年龄,年龄相同比较姓名),参数传递方式使用lambda表达式的形式。
    //定义员工信息list
List<User> 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>且T为参数,R为返回值,同时在该接口中声明对应的抽象方法;
b.在类TestLambda_3中声明一个成员方法calculate()并使用a中的接口作为参数,输出员工信息。
    /**
* 该成员方法中形参1表示的是需要被操作的字符串,形参2表示的是接口中的操作实现。
* */
public List<User> calculate(MyPractice_2<List<User>> mp){
mp.calculateValues();
return user_list;
} @Test
public List<User> test_3(){
List<User> 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);
  2. Supploer< T> 供给型接口;

    //抽象方法
    T get();
  3. Function< T> 函数型接口;

    //抽象方法
    R apply(T t);
  4. 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<String> list_1 = new ArrayList<>();
Stream<String> stream_1 = list_1.stream();
//可以通过of()创建Stream的
Stream<String> stream_2 = Stream.of("aa","bb","cc");
//创建无限流
Stream<Integer> 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<Integer> 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所示:

图3-1

  1. 查找
 /**
* 查找
* findFirst:查找流中的第一个元素
* findAny:查找流中的任意一个元素
* count:返回流中元素的总个数
* */
@Test
public void testMatch(){
//比较过后获取流中第一个元素,并放入Optional容器中
Optional<User> op = user_list.stream()
.sorted((e1,e2) -> -Double.compare(e1.getAge(), e2.getAge()))
.findFirst();
System.out.println(op.get());
//从流中取出任意一个符合条件的元素,并放入Optional容器中
Optional<User> 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<Integer> op_3 = user_list.stream()
.map(User::getAge)
.min(Integer::compare);
System.out.println(op_3.get());
}

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

图3-2

  1. 排序
  /**
* 排序
* 1、sorted:自然排序(Comparable);
* 2、sorted(Comparator com):定制排序。
* */
@Test
public void test_1(){
List<Integer> 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);
}

以上就是我在学习 Java 8新特性的学习笔记,如有不足和错误,期待大家的指正和交流。

【Java 进阶】Java8 新特性的理解与应用的更多相关文章

  1. 【Java】Java8新特性

    文章目录 Java8新特性 Lambda表达式的使用 语法格式一:无参,无返回值 语法格式二:Lambda 需要一个参数,但是没有返回值. 语法格式三:数据类型可以省略,因为可由编译器推断得出,称为& ...

  2. [Java SE]Java8新特性——默认方法

    1 简述 默认方法就是接口可以有实现方法,而且可以不需要实现类去实现其方法 默认方法(default void hello()) := 一个在接口里面有了一个(默认)实现的方法 1. 子类优先继承父类 ...

  3. Java基础-Java8新特性

    一.Lambda表达式 在了解 Lambda 之前,首先回顾以下Java的方法. Java的方法分为实例方法,例如:Integer的equals()方法: public final class Int ...

  4. java基础---java8 新特性

    1. 函数式接口 函数式接口主要指只包含一个抽象方法的接口,如:java.lang.Runnable(java1.0).java.util.Comparator接口(java1.4)等. Java8提 ...

  5. java8新特性学习笔记

    目录 1.速度更快 2.Lambda表达式 2.1.匿名内部类的Lambda转换 2.2.java8内置的四大核心函数式接口 2.3.方法引用和构造器 2.3.1.方法引用 2.3.2.构造器引用 2 ...

  6. Java系列 - 用Java8新特性进行Java开发太爽了

    本人博客文章网址:https://www.peretang.com/using-java8s-new-features-to-coding-is-awesome/ 前言 从开始写博客到现在已经过去3个 ...

  7. Java学习之==>Java8 新特性详解

    一.简介 Java 8 已经发布很久了,很多报道表明Java 8 是一次重大的版本升级.Java 8是 Java 自 Java 5(发布于2004年)之后的最重要的版本.这个版本包含语言.编译器.库. ...

  8. [Java8教程]Java8新特性进阶集合

    Java8新特性进阶集合 基于 AOP 抽离方法的重复代码 Java8:当 Lambda 遇上受检异常 Java8:对字符串连接的改进 Java8:Java8 中 Map 接口的新方法 Java8:当 ...

  9. 【Java基础】Java8 新特性

    Java8 新特性 Lambda 表达式 Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递).使用它可以写出更简洁.更灵活的代码. L ...

  10. 【Java学习笔记之二十八】深入了解Java8新特性

    前言: Java8 已经发布很久了,很多报道表明java8 是一次重大的版本升级.在Java Code Geeks上已经有很多介绍Java 8新特性的文章,例如Playing with Java 8 ...

随机推荐

  1. idea的mybatis插件free mybatis plugin(或 Free MyBatis Tool),很好用

    为大家推荐一个idea的mybatis插件----free mybatis plugin(或 Free MyBatis Tool),很好用(个人觉得free mybatis plugin更好用一点,可 ...

  2. 手撕Vuex-安装模块数据

    前言 根据上一篇,[手写Vuex]-提取模块信息,我们已经可以获取到模块的信息了,将模块信息变成了我们想要的数据结构,接下来我们就要根据模块的信息,来安装模块的数据. 在上一篇当中我们定义了一个 Mo ...

  3. 【Javaweb】瑞吉外卖你冲不冲?冲冲!冲!冲冲!(数据库环境搭建)(maven项目搭建)一

    图形界面创建数据库(Navicat) 命令行方式创建 瑞吉项目一共涉及到十一张表 导入表结构,既可以使用上面的图形界面,也可以使用MySQL命令: 通过命令导入表结构时,注意sql文件不要放在中文目录 ...

  4. 2023年的PHP项目部署笔记。什么?还有人用PHP?

    前言 这是我第一次用 PHP 的包管理工具 composer 一开始用 docker 进行部署,但一直出问题,最后还是选择直接在服务器上安装 php-fpm 搭配 nginx 的方案了. PS:doc ...

  5. Http请求超好用的工具类

    话题不多说,直接开整 1.先导入依赖 <dependency> <groupId>io.github.admin4j</groupId> <artifactI ...

  6. VMware安装虚拟机详细步骤

    在VMware中安装CentOS7 01.目录 CentOS7的下载 CentOS7的配置 CentOS7的安装 CentOS7的网络配置 自动获取IP 固定获取IP 02.安装前提 准备工作: 提前 ...

  7. Redis入门实践

    安装Redis 下载:官网:https://redis.io/download/,选择稳定版下载. 上传至linux 解压Redis:tar -zxvf redis-6.2.7.tar.gz,得到: ...

  8. MySQL运维12-Mycat分库分表之按天分片

    一.按天分片 指定一个时间周期,将数据写入一个数据节点中,例如:第1-10天的数据,写入到第一个数据节点中,第2-20天的数据写入到第二个节点中,第3-30天的数据节点写入到第三个数据节点中. 说明1 ...

  9. 为什么说数字孪生和GIS高度互补?它们各自从对方那里获得了什么?

    在数字化时代,数字孪生和GIS作为两项重要技术,它们的融合正日益受到人们的关注和认可.数字孪生是将实体世界与数字世界紧密结合的技术,可以创建实时的虚拟副本,对物理系统进行模拟.优化和预测.而GIS则是 ...

  10. Github 星标 8K+ 这款国人开源的 Redis 可视化管理工具,真香...

    做程序员就少不了与一些工具打交道,比如:监控工具.管理工具等,有些工具是命令行界面,有些工具是可视化界面,反正都是可以能够满足日常使用的功能需求. 对于redis管理工具来说,也有不少可能的产品,比如 ...