一文带你攻克JDK新特性

news2024/11/17 6:20:39

1.Java8 新特性介绍

1.1 实验介绍

在国内,Java8 是当之无愧的普及率最高的 JDK 版本,从笔者工作开始, 就一直使用的是 JDK8 的版本,虽然现在 JDK19 即将面世,但是似乎依旧无法 动摇 JDK8 在国内的地位。这里面最主要的原因就是 JDK8 足够稳定,功能足够 优秀,而替换 JDK 版本会有潜在的风险。既然 JDK8 如此稳定,就意味着需要 很好的掌握 JDK8 中的特性,本次实验就会对 JDK8 做一个详细的介绍,并通过 一些简单例子做个基础的入门。

知识点

1. JDK 的版本化

2. Lambda 表达式

3. 函数式接口

4. 方法引用与构造器引用

5. Stream 表达式

6.接口中的默认方法和静态方法

7. Optional

8. 新的时间日期 API

1.2 JDK 的版本化

在正式开始本次实验之前,有个概念需要了解。JDK 在发展过程中,分为 LTS 版 本和 non-LTS 版本,其中 LTS 版本表示这是一个长期支持的版本,而 non-LTS 表示这是一个不会长期支持的版本。

从上面这张图可以看到,目前的 JDK 版本中,8、11、17、21 会是被长期支持 的版本,并且对 JDK8 的支持时间达到了 2030 年,比 JDK11 和 JDK 17 还要 长,因此学好 JDK8 更加重要了。

 1.3Lambda 表达式

Lambda 表达式是 JDK8 中最大的一次更新,Lambda 是一个匿名函数,它允许你 通过表达式来代替功能接口,使用 Lambda 可以让代码变得更加简洁。简单来讲, Lambda 使用更简洁的方式给接口添加实现方法。 比如下面这段代码,以前是这样写的,Runnable 后的匿名类需要被完整的写出 来。

public class AnonymousDemo {

public static void main(String[] args) { Runnable runnable = new Runnable() {

@Override

public void run() { System.out.println("do something"); } }; Thread thread = new Thread(runnable); thread.start(); } }

当 Lambda 出现后,这种写法就变成了下面这样

public class AnonymousDemo {

public static void main(String[] args) {

new Thread(() -> System.out.println("do something")).start(); } }

是不是一下子变简单了?

1.4 函数式接口

函数式接口就是那些只有一个抽象方法的接口,JDK8 中内置了四种函数式接口:

Consumer : void accept(T t); Supplier : T get(); Function : R apply(T t); Predicate : boolean test(T t)

这四种内置的函数式接口提供了四种不同类型的代码写法,以 Consumer 为例。 Consumer 是一种消费型接口,提供了一个参数、无返回值的接口方法,就和消 费一样,花出去就没有了。

import java.util.function.Consumer;

public class ConsumerTest {

public static void main(String[] args) { consume(100,money -> { System.out.println("消费了"+money+"元"); }); }

public static void consume(double money, Consumer consumer){ consumer.accept(money); } }

在上面的例子中,consume 方法有两个入参,一个 double 类型的金额和一 个 Consumer 函数接口,方法的实现就是执行 Consumer 函数接口默认的 accept 方法。在使用 consume 方法中,就可以直接通过 Lambda 表达式来实现 函数式接口的调用,让代码更加简洁。

1.5 方法引用与构造器引用

方法引用使得开发者可以直接引用现存的方法、Java 类的构造方法或者实例对 象。构造器引用和方法引用类似,主要用途往往是创建对象。方法引用和构造器 引用主要是配合 Lambda 表达式,使得表达式变得更加简单。 比如要输出一个 List 中的数据,使用方法引用的话通 过 System.out::println 就实现了。

import java.util.Arrays;import java.util.List;

public class Test2 {

public static void main(String[] args) { List list = Arrays.asList("a","b"); list.forEach(System.out::println); } }

1.6 stream 表达式

JDK8 中两个最重大的改变,一个是 Lambda 表达式,另外一个就是 Stream API。Stream 是 JDK8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的 操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用 Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。 也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种 高效且易于使用的处理数据的方式。 比如我要获取 List 中大于 50 的数据,就可以这样写

import java.util.Arrays;import java.util.List;import java.util.stream.Co llectors;public class StreamTest {

public static void main(String[] args) { List list = Arrays.asList(1,2,3,51,52,53); List filterList = list.stream().filter(x -> x>50).collec t(Collectors.toList()); fliterList.forEach(System.out::println); } }

上面这段代码将大于 50 的数据传到了 filterList 中并输出。大家可以打开右 边的实验环境验证最终的输出结果。

1.7 接口中的默认方法和静态方法

在 JDK8 之前,接口中的方法只能声明无法实现,在 JDK8 中,接口中可以添加 默认方法和静态方法了。 首先介绍默认方法,通过 default 修饰的方法可以在接口中增加实现。

public interface MyInterface {

default void defaultMethod(){ System.out.println("hello"); } }

另外是接口中的静态方法:

public interface MyInterface {

public static void staticMethod(){ System.out.println("hello"); } }

更多的介绍会在后面的实验中给出。

1.8 Optional

Optional 类是 JDK8 的新特性,是一个可以为 null 的容器对象,Optional 往 往被用来做空值的判断。 集合类型的判空在某些场景下会十分啰嗦,比如当你接收到的 Map 是这样的

{"user":{"info":{"address":"hz"}}}

这种时候如果按照常规的写法,需要写多层 if 语句进行判空

if (map.get("user")!=null){ Map user = (Map) map.get("user");

if (user.get("info")!=null){ Map info = (Map) user.get("info");

if (info.get("address")!=null){ String address = (String) info.get("address"); System.out.println(address); } } }

if 里面套着 if,结构十分复杂,这个时候我们就可以使用 Optional

String address=Optional.ofNullable(map) .map(m->(Map)m.get("user")) .map(user->(Map)user.get("info")) .map(info->(String)info.get("address")) .orElse(null);

一下子就变得简洁明了。

1.9 新的时间日期 API

时间类一直是代码开发中经常用到的东西,时间类更新到 JDK8 版本期间,一共 迭代了三次。分别是 Date 类、Calendar 类和 LocalDateTime 类。 这一次推出来的 LocalDateTime 类,终于让 Java 中的时间类变得很好用了

import java.time.Instant;import java.time.LocalDate;import java.time.Loc alDateTime;import java.time.LocalTime;

public class DateTest {

public static void main(String[] args) { Instant now = Instant.now();

//获取纳秒级别的时间戳

System.out.println(now.toEpochMilli());LocalDate localDate=LocalDate.now(); LocalTime localTime=LocalTime.now(); LocalDateTime ldt = LocalDateTime.now(); System.out.println(localDate); System.out.println(localTime); System.out.println(ldt); } }

大家能猜出上面的这段代码会返回怎样的数据吗?

1.10 实验总结

本次实验对 JDK8 中的一些重要特性做了简单的介绍,可以看到这些新特性让 Java 变得更加有趣了。在接下来的实验中,针对上述的内容会有更详细的介绍。

2.学透 Lambda 表达式

2.1 实验介绍

Java8 是一个跨时代的更新,在这个版本中,出现了很多新特性,其中 Lambda 表 达式就是 Java8 中十分重要的新特性。本次实验将基于 Java8 详细介绍 Lambda 表达式的使用,简化代码的编写。

知识点

1. 匿名类

2. 函数式接口

3. Lambda 表达式介绍

4.Lambda 表达式的变量作用域

5.Lambda 表达式在实际中的应用

2.1 匿名类

在正式介绍 Lambda 表达式之前,首先需要知道的一个知识点是匿名类。 因为 Java 中的 Lambda 表达式最重要的用法就是简化匿名类的使用。匿名类 可以同时声明和实例化一个类,匿名类和本地类十分相似,只是匿名类没有名称。 比如下面有一个叫 Person 的接口,里面有一个 sayHello 方法

public interface Person {

void sayHello(); }

接着创建一个类 Son 实现 Person 接口,实现里面的 sayHello 方法。 在不用匿名类的情况下,就需要新建一个类实现 Person,然后重写 sayHello 方 法,就像下面这样

public class Son implements Person{

@Override

public void sayHello() { System.out.println("son say hello"); } }

如果这个 Son 类只是用来在某个类中执行特定任务的话,就可以使用更加简洁 的匿名内部类写法,而无需创建这个 Son 类

public class AnonymousDemo {

public static void main(String[] args) { Person person = new Person(){

@Override

public void sayHello() { System.out.println("anonymous say hello"); } }; person.sayHello(); } }

在上面这个例子中,虽然没有创建实体的类,但是通过一个匿名类的方式实现了 本地类的效果。总结下来,匿名类是不能有名字的类,它们不能被引用,只能在 创建时用 new 语句来声明它们。

2.2 函数式接口

在正式学习 Lambda 表达式之前,还有一个很重要的概念需要了解,那就 是函数式接口。函数式接口主要就是给 Lambda 表达式使用的。 函数式接口可以使用 @FunctionalInterface 来修饰,如果一个接口是函数式接 口,那么这个接口就可以使用 Lambda 表达式来写。 函数式接口有下面几个特征:

. 只能有一个抽象方法。

. 允许定义默认方法

. 允许定义静态方法

. 允许定义 java.lang.Object 里的 public 方法

拿 java.util 包下的 Comparator 接口举个例子,下面是这个接口的一部分代码

@FunctionalInterfacepublic interface Comparator {

int compare(T o1, T o2);

boolean equals(Object obj);

default Comparator reversed() {

return Collections.reverseOrder(this); }

public static super T>> Comparator reverse Order() {

return Collections.reverseOrder(); } }

从代码中可以看到,Comparator 完美符合了函数式接口的特性,只能有一个抽 象方法 compare,有默认方法,有静态方法,有 java.lang.Object 里的 public 方 法。

2.3 Lambda 表达式

匿名类的一个问题是,如果匿名类的实现非常简单,例如只包含一个方法的接口, 那么匿名类的语法可能看起来笨拙且不清楚。因此 Lambda 表达式出现了,

Lambda 允许通过表达式来代替功能接口,使用 Lambda 可以让代码变得更加 简洁。

Lambda 只能在函数式接口中使用,上面这段使用匿名类的方法,换成 Lambda

表达式之后就变成了下面这样:

public class AnonymousDemo {

public static void main(String[] args) { Person person = () -> System.out.println("anonymous say hello"); person.sayHello(); } }

一下子变得更加简单了。

Lambda 表达式的语法格式如下所示:

(parameters) -> expression

(parameters) ->{ statements; }

lambda 表达式的写法主要有下面几种

无参数,无返回值

开头的例子中就是一个无参数和无返回值的写法,在这个例子中,接口中定 义的抽象方法没有入参也没有返回值:

public class AnonymousDemo {

public static void main(String[] args) { Person person = () -> System.out.println("anonymous say hello"); person.sayHello(); }}

有参数,无返回值

先写一个这样的接口:

public interface Student {

void getAge(int age); }

在以前的代码中,需要首先写一个类去继承这个接口,或者是写一个匿名内部类, 如:

Student student=new Student() {

@Override

public void getAge(int age) { System.out.println(age); } };

使用 Lambda 就变得简单了

Student student2=(age) -> System.out.println(age);

如果只有一个参数,小括号可以不写

Student student3=age -> System.out.println(age);

如果想要在 Lambda 表达式中写一些复杂的方法,在语法上可以在大括号内部 写实现方法。

public class Main {

public static void main(String[] args) { Student student4 = age -> {

// do something

System.out.println(age); }; student4.getAge(10); } }

上面的这段代码最终会输出 10。

有参数,有返回值

首先写一个有参数,有返回值的接口方法

public interface Student {

int getAge(int age); }使用 Lambda 表达式时代码如下

public class Main {

public static void main(String[] args) { Student student = age -> {

return age * 5;};

int age = student.getAge(10); System.out.println(age); } }

在上面的代码中,由于 Student 的 getAge 方法是有返回值的,因此在写带有 大括号的实现方法时也必须带上 return。 如果实现方法用单行代码就能写完,可以将大括号和 return 进行简化,代码就 变成了下面这样:

public class Main {

public static void main(String[] args) { Student student = age -> age * 5;

int age = student.getAge(10); System.out.println(age); } }

2.4 Lambda 表达式的变量作用域

Lambda 内部如果使用了外部定义的局部变量,那这个变量是一定不能被修改的, 看下面这段代码

public class AnonymousDemo {

public static void main(String[] args) {

int num = 1; Person person = ()->{

int a = num; }; num++; } }

在 Lambda 表达式中使用了 num,又在外部对 num 进行了修改,就会出现下 面这段报错

Variable used in lambda expression should be final or effectively final

不过如果这个变量是对象变量或者静态变量,那么就没有不能修改的限制了。

public class AnonymousDemo {

static int num = 1;

public static void main(String[] args) { Person person = ()->{

int a = num; }; num++; }}

2.5 Lambda 表达式在实际中的应用

在 JDK8 中,有许多接口都可以使用 Lambda 表达式来简化代码的编写,接下 来介绍几个常见 Lambda 的使用场景。

Runnable 接口

从 Runnable 接口的源码中可以看到,这个接口是用 @FunctionalInterface 修 饰的,意味着它可以使用 Lambda 表达式。 普通的匿名类写法是这样的:

public class AnonymousDemo {

public static void main(String[] args) { Runnable runnable = new Runnable() {

@Override

public void run() { System.out.println("do something"); } }; Thread thread = new Thread(runnable); thread.start(); } }

使用 Lambda 表达式后,写法就变成了下面这样

public class AnonymousDemo {

public static void main(String[] args) { Runnable runnable = () -> System.out.println("do something"); Thread thread = new Thread(runnable); thread.start(); } }

如果再精简一点,使用一行代码就可以搞定

public class AnonymousDemo {

public static void main(String[] args) {

new Thread(() -> System.out.println("do something")).start(); } }

Comparator 接口

Comparator 是一个用来排序的接口,比如可以对 List 集合进行自定义排序:

public class AnonymousDemo {

public static void main(String[] args) { List list = Arrays.asList(3,4,2,1,6,7,9); Collections.sort(list, new Comparator() {

@Override

public int compare(Integer i1, Integer i2) {

return i1.compareTo(i2); } }); } }

而使用 Lambda 写法就会让上面的代码变得十分简单。

public class AnonymousDemo {

public static void main(String[] args) { List list = Arrays.asList(3,4,2,1,6,7,9); Collections.sort(list, (i1, i2) -> i1.compareTo(i2)); } }

2.6 实验总结

总结来讲,Lambda 表达式的使用会让代码看起来更加清晰,最好的学习方式就是实践了, 对着上面的例子多动手敲一些代码,会使你对 Lambda 的使用变得炉火纯青。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/149878.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

leetcode-10:还原排列的最少操作步数

原题描述如下: 给你一个偶数 n​​​​​​ ,已知存在一个长度为 n 的排列 perm ,其中 perm[i] i​(下标 从 0 开始 计数)。 一步操作中,你将创建一个新数组 arr ,对于每个 i : 如…

蓝桥杯-地宫取宝

算法分类: 动态规划 dp 问题描述 X 国王有一个地宫宝库,是 nm 个格子的矩阵,每个格子放一件宝贝,每个宝贝贴着价值标签。 地宫的入口在左上角,出口在右下角。 小明被带到地宫的入口,国王要求他只能向右…

使用DBeaver 连接时序数据库TDengine

介绍 TDengine 是一款高性能、分布式、支持 SQL 的时序数据库 (Database)。 DBeaver 是一款流行、开源的数据库管理工具以及 SQL 客户端,其功能强大,并且支持任何拥有 JDBC-Driver 的数据库(这意味着几乎所有数据库都支持)。 只…

Java面试常见问题-JVM篇

JVM面试问题汇总①什么是字节码,采用字节码的好处是什么⭐java类加载器有哪些⭐双亲委派模型⭐⭐GC是如何判断对象可以被回收总结了目前主流平台中常见的面试题,标⭐为重点! 第一次更新节点:2023.1.8 什么是字节码,采用…

用nvidia-smi查看GPU的状态时,能耗pwr显示为ERR!

用nvidia-smi查看GPU的状态时,能耗pwr显示为ERR! 解决方式: 以下代码查看具体的报错: dmesg -l err 如果有: NVRM:***说明硬件问题,需要更换 如果无错误,则参考如下: 1. 将你的工作站或者服务器报错的…

车载激光雷达赛道「新窗口」

车载激光雷达的降本逻辑,除了前装量产规模的加速,还有背后核心供应链的驱动。这也被视为激光雷达新周期的核心竞争力。 Lumotive是一家由微软创始人比尔盖茨投资的初创公司,为激光雷达公司提供基于波束转向技术的核心元器件,采用液…

【寒假每日一题】DAY1.水仙花数

一、题目描述 求0~100000之间的所有“水仙花数”并输出。 什么是水仙花数: “水仙花数”是指一个n位数,其各位数字的n次方之和确好等于该数本身,如:153=1^3+5^3+3^3,则153是一个“水…

前端vue中ts无法识别引入的vue文件,提示找不到xxx.vue模块的解决【引入新建页面或者通过router引入时报错】

一、文章引导 #mermaid-svg-7KdCeocZ1DbrWrKC {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-7KdCeocZ1DbrWrKC .error-icon{fill:#552222;}#mermaid-svg-7KdCeocZ1DbrWrKC .error-text{fill:#552222;stroke:#55222…

联合证券|“仰望”概念爆发,多股涨停!人气龙头股罕见“炸板”

今日涨停股中,以封单金额核算,今飞凯达、联泓新科、灵通动力等涨停板封单资金最多。 涨停家数回落 1月6日,沪深两市股价收盘涨停的有37只,跌停股有14只。下午盘面震动,炸板数量增多,兔宝宝、中远海科、久其…

64. 锚框

1. 锚框 锚框是用来预测真实的边缘框。 2. loU-交并比 ioU用来计算两个框之间的相似度 0表示无重叠,1表示重合 这是Jacquard指数的一个特殊情况 给定两个集合A和B: 杰卡德系数(Jaccard)可以衡量两组之间的相似性。 给定集合A和…

D. Boris and His Amazing Haircut(线段树)

传送门题意:给定长度为 n 的数组 A ,代表 Boris 现在的头发长度,和一个长度为 n 的数组 B ,代表他希望的发型的头发长度。理发师手里有 m 把剪刀,每个都只能用一次,剪刀的所剪的高度用 xi 给出。 对于每一把…

ASO优化之苹果和安卓的优化思路

大家都知道,ASO优化是指应用商店的搜索优化,其目的就是通过关键词的覆盖,让APP的搜索排名靠前,带来更多的曝光和用户下载量。 安卓ASO优化的思路: 1,关键词的覆盖。由于现在大部分的安卓商店为了商业化,所以后台几乎…

【C语言进阶】指针经典笔试题

作者:匿名者Unit 目录指针笔试题eg1.eg2.eg3.eg4.指针笔试题 eg1. 我们先来看第一题: int main() {int a[4] { 1, 2, 3, 4 };int *ptr1 (int *)(&a 1);int *ptr2 (int *)((int)a 1);printf( "%x,%x", ptr1[-1], *ptr2);return 0; }我们先来分析ptr1&…

初级篇Nginx笔记

第一章、Nginx的目录结构以及运行原理Nginx的目录结构[rootlocalhost ~]# tree /usr/local/nginx/usr/local/nginx├── client_body_temp # POST 大文件暂存目录├── conf # Nginx所有配置文件的目录│ ├── fastcgi.conf # fastcgi相关参数的配置文件│ ├── fastcgi.…

在maven项目当中创建第一个jdbc程序

大家好&#xff0c;今天给大家分享在Maven环境中创建jdbc程序 这是Maven项目的创建方式 跟着一步一步做就可以了 然后&#xff0c;创建好的Maven项目应该是这样的 导入相关的依赖 <dependencies><!-- https://mvnrepository.com/artifact/mysql/mysql-connector-ja…

PHP date() 函数

PHP date() 函数用于格式化时间/日期。 PHP date() 函数 PHP date() 函数可把时间戳格式化为可读性更好的日期和时间。 时间戳是一个字符序列&#xff0c;表示一定的事件发生的日期/时间。 语法 string date ( string $format [, int $timestamp ] ) 参数描述format必需。规…

Python下载ts文件视频且合并

目录 一、ts文件的由来 二、下载ts文件 1.下载index.m3u8&#xff0c;并做相应处理 2.下载ts文件 三、合并ts文件 一、ts文件的由来 ts文件&#xff0c;ts即"Transport Stream"的缩写&#xff0c;特点就是要求从视频流的任一片段开始都是可以独立解码的&#xff…

Pytorch 权重衰减

目录 1、权重衰减 2、L2正则化和L1正则化 3、高维线性回归演示权重衰减 1、权重衰减 一般来说&#xff0c;我们总是可以通过去收集更多的训练数据来缓解过拟合。 但这可能成本很高&#xff0c;耗时颇多&#xff0c;或者完全超出我们的控制&#xff0c;因而在短期内不可能做到…

OpenTelemetry日志体系

前言 OpenTelemetry为了实现其可观测性有三大体系&#xff1a;Trace&#xff0c;Metrics和Log。本文将对于OpenTelemetry实现的日志体系进行详细的讲述。 日志 说到日志相比大家都能侃侃而谈&#xff1a;帮助快速定位出现的问题&#xff0c;数据追踪&#xff0c;性能分析等等…

联合证券|新年再现“A吃A” 建发股份拟控股美凯龙

2022年&#xff0c;A股上市公司之间的并购超越10起。2023年伊始&#xff0c;上市公司“A吃A”或将再添新事例。 建发股份(600153)1月8日晚公告&#xff0c;公司正在谋划经过现金方法协议收购红星美凯龙控股集团有限公司&#xff08;以下简称“红星控股”&#xff09;持有的美凯…