【进阶】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. 一篇文章让你理解:什么是Spring???

    背景 市场上,随便一个Java工程师的招牌要求上,都可以看到SSM.Spring.SpringMVC...类似字样.这玩意到底是个啥? 这是中邮消费招聘的岗位要求,可以看到第3点: 3.熟悉Strut ...

  2. Python实现对word批量操作

    Python在平时写写小工具真是方便快捷,Pyhon大法好. 以下所有代码都是找了好多网上的大佬分享的代码按照自己的需求改的. 调用的库为Python-docx.win32com.PyPDF2.xlw ...

  3. python之特殊属性和特殊方法

    目录 特殊属性 __dict__查看属性和方法 __class__查看对象所属类 __bases__查看子类的父类 __mro__查看类的层次结构 __subclasses__查看父类被继承的子类 特 ...

  4. .NET 与 OpenEuler 共展翅,昇腾九万里

    openEuler 已支持 X86.ARM.SW64.RISC-V.LoongArch 多处理器架构,逐步扩展 PowerPC 等更多芯片架构支持,持续完善多样性算力生态体验. openEuler 社 ...

  5. 初探webpack之单应用多端构建

    初探webpack之单应用多端构建 在现代化前端开发中,我们可以借助构建工具来简化很多工作,单应用多端构建就是其中应用比较广泛的方案,webpack中提供了loader与plugin来给予开发者非常大 ...

  6. Flink State 状态原理解析

    一.Flink State 概念 State 用于记录 Flink 应用在运行过程中,算子的中间计算结果或者元数据信息.运行中的 Flink 应用如果需要上次计算结果进行处理的,则需要使用状态存储中间 ...

  7. HDU 4787 GRE Revenge

    Now Coach Pang is preparing for the Graduate Record Examinations as George did in 2011. At each day, ...

  8. Oracle数据库卸载器 - 开源研究系列文章

    今天无事,把网上搜到的Oracle数据库卸载器的软件更新到C#的Winform界面的操作上. 1. 程序目录: 与笔者的其它软件类似,目录如下: 2. 使用的类: 这里主要使用了一个处理函数: 3. ...

  9. 蓝桥杯-最短路 (SPFA算法学习)

    SPFA算法主要用来解决存在负边权的单源最短路情况(但不能有负环!!!)一个简单的方法判断是否有没有负环可以通过判断是否有一个节点是否频繁进出队列. 以下内容转自https://blog.csdn.n ...

  10. 关于WPF下用户登录后再启动主窗体的实现方法

    /// <summary>App.xaml 的交互逻辑</summary> public partial class App : Application { private b ...