java8新特性知识整理
前言
jdk1.8 中新特性包含:
Lambda 表达式 随着大数据的兴起,函数式编程在处理大数据上的优势开始体现,因此引入了 Lambada 函数式编程
函数式接口 函数式接口的提出是为了给 Lambda 表达式的使用提供更好的支持
接口中的默认方法和静态方法
方法引用和构造器调用 若 Lambda 体中的内容有方法已经实现,那么就可以使用方法引用,方法引用再次简化 Lambda 操作
Stream API 使用 Stream 彻底改变了集合使用方式,只关注结果、不关心过程
新日期/时间 API java.time 包下
新的客户端图形化工具界面库:JavaFX
Java 与 JS 交互引擎 -nashorn
其他特性
jdk10 中新特性包含:
- var 是一种动态类型,用来定义局部变量的
Lambda 表达式
概念
Lambda 是带有参数变量的表达式,允许将一段代码当成参数传递给某个方法;本质上是匿名内部类,java 刚开始使用匿名内部类代替 Lambda 表达式,它其实就是把匿名内部类中一定要做的工作省略掉,然后由 JVM 通过推导把简化的表达式还原,这样可以写出更简洁、更灵活的代码
格式
() -> {}
(parameters 参数) -> {expression 表达式或方法体}
说明
变量如果是可以预知的,就可以省略;如果只有一个参数,()可以省略;如果代码块只有一行表达式,{}可以省略;
可以引用类成员和局部变量,但是会将这些变量隐式的转换成 final
局部变量限制
Lambda 表达式也允许使用自由变量(不是参数,而是在外层作用域中定义的变量),就像匿名类一样。 它们被称作捕获 Lambda。 Lambda 可以没有限制地捕获(也就是在其主体中引用)实例变量和静态变量。但局部变量必须显式声明为 final,或事实上是 final。
为什么局部变量有这些限制?
(1)实例变量和局部变量背后的实现有一个关键不同。实例变量都存储在堆中,而局部变量则保存在栈上。如果 Lambda 可以直接访问局部变量,而且 Lambda 是在一个线程中使用的,则使用 Lambda 的线程,可能会在分配该变量的线程将这个变量收回之后,去访问该变量。因此, Java 在访问自由局部变量时,实际上是在访问它的副本,而不是访问原始变量。如果局部变量仅仅赋值一次那就没有什么区别了——因此就有了这个限制。
(2)这一限制不鼓励你使用改变外部变量的典型命令式编程模式。
- 表达式有返回值,返回值的类型由编译器推理得出。
举例
List<String> list =Arrays.asList("aaa","fsa","ser","eere");
// 简化前,使用匿名内部类的方式,重写接口方法;
// 另一种方式:使用思路是设计模式;实现接口重写方法,每次只需要将实现类的实例传入即可,就可以达到不同的验证条件,
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);
}
})
// 简化后
Collections.sort(list, (a,b)->b.compareTo(a));
方法引用
使用 lambda 表达式会创建一个匿名方法,但有时候使用 lambda 表达式只调用一个已经存在的方法,因此才引入方法引用,方法引用的用途是支持 Lambda 表达式的简写。
概念
方法引用,使用双冒号(::)运算符,目标引用对象放在分隔符::前,方法名称放在后面;开发者可以直接引用现存的方法、Java 类的构造方法或者实例对象;
分类
// 静态方法引用
Person[] rosterAsArray = new Person[30];
Arrays.sort(rosterAsArray, Person::compareByAge);
// 构造器引用,Class<T>::new; 需要注意的是这个构造器没有参数,String::new,对应的Lambda:() -> new String()
// 对象的实例方法引用,System.out::println(); out 是 System 的实例对象,然后引用方法 println,类要先实例化
list.stream().sorted(Comparator.comparing(TestDemo1::getAge)).collect(Collectors.toList());
// 成员方法引用, Class::method,String::valueOf,对应的 Lambda:(s) -> String.valueOf(s)
函数式接口
当一个接口中存在多个抽象方法时,如果使用 lambda 表达式,并不能智能匹配对应的抽象方法,因此引入了函数式接口的概念。接口中只有一个抽象方法的接口,使用注解 @FunctionalInterface 修饰的,称为函数式接口。
常用的接口:java.lang.Runnable 和 java.util.concurrent.Callable 和 Comparator 接口
接口中可以定义静态方法
java.util.Collection接口添加新方法,如stream()、parallelStream()、forEach()和removeIf()等等
常见的四大函数式接口
Consumer 消费型接口,有参无返回值
Supplier 供给型接口,无参有返回值
Function<T, R> 函数式接口,有参有返回值
Predicate 断言型接口,有参有返回值,返回值是 boolean 类型
Stream 流
概念
流是 Java API 的新成员,它允许我们以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现)。Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,我们可以把它们看成遍历数据集的高级迭代器。单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。而和迭代器又不同的是,Stream 可以透明地并行化操作,也就是说我们不用写多线程代码了。迭代器只能命令式地、串行化操作。顾名思义,当使用串行方式去遍历时,每个 item 读完后再读下一个 item。而使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。Stream 的并行操作依赖于 Java7 中引入的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程。
构造流的几种方式
// 1. Individual values
Stream stream = Stream.of("a", "b", "c");
// 2. Arrays
String [] strArray = new String[] {"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
// 3. Collections
List<String> list = Arrays.asList(strArray);
stream = list.stream();
常用 api
// forEach
list.stream.forEach(item -> System.out.println(item));
map.forEach((k,v) -> System.out.println(k+v)) // forEach遍历map
// fifter
list.stream.fifter(item -> !"aa".equals(item)).collect(Collet.toList)
// 去重 distinct toSet
list.stream.map(**::getXxx).distinct.collect(collect.toList)
// map获取实例中的某个元素,转化为对象的集合
list.stream.map(**::getXx).collect(Collect.toList)
// 单个字段排序 (降序:reversed())
list.sort((a,b) -> a.getXxx.compareTo(b.getXxx))
list.sort(Comparator.conparing(**:getXxx))
// 多个字段排序
thenComparing
// List<Map<String, Object>>转Map<String, List<Map<String, Object>>>
Map<String, List<Message>> groupByMsg = list.stream().collect(groupingBy(Message::getMsg));
// List求和、求最大值、平均值
Long sum= list.stream().mapToLong(Message::getId).sum();
Optional<Message> maxMassage = list.stream().collect(Collectors.maxBy(Comparator.comparing(Message::getId)));
Long maxId = maxMassage.get().getId();
LongSummaryStatistics lss = list.stream().collect(Collectors.summarizingLong(Message::getId));
Collectors.toMap (List 转 Map)
Map<Long, String> map = userList.stream().collect(Collectors.toMap(User::getId, User::getName));
Map<Long, User> userMap = userList.stream().collect(Collectors.toMap(User::getId, t -> t));
Map<Long, User> userMap = userList.stream().collect(Collectors.toMap(User::getId, Function.identity()));
// Collectors.toMap 有三个重载方法:
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper);
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction);
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier);
// 参数含义分别是:
// keyMapper:Key 的映射函数
// valueMapper:Value 的映射函数
// mergeFunction:当 Key 冲突时,调用的合并方法
// mapSupplier:Map 构造器,在需要返回特定的 Map 时使用,比如希望返回的 Map 是根据 Key 排序的
userList.stream().collect(Collectors.toMap(User::getId, User::getName, (n1, n2) -> n1 + n2));
peek 和 map 区别
peek 的入参为 Consumer :Stream peek(Consumer<? super T> action);
map 的入参为 Function : Stream map(Function<? super T, ? extends R> mapper);
peek 和 map 的返回值都是 Stream,peek 无法改变返回值类型,而 map 因为内部入参的原因,是可以改变返回值类型的
groupingBy (List 转 Map)
// 分组
Map<String, List<Product>> prodMap = prodList.stream().collect(Collectors.groupingBy(Product::getCategory);
Map<Integer, List<String>> resultList = couponList.stream().collect(Collectors.groupingBy(Coupon::getCouponId,Collectors.mapping(Coupon::getName,Collectors.toList())));
// 组合分组
Map<String, List<Product>> prodMap = prodList.stream().collect(Collectors.groupingBy(item -> item.getCategory() + "\_" + item.getName()));
// 求总数
Map<String, Long> prodMap = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.counting()));
// 求和
Map<String, Integer> prodMap = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.summingInt(Product::getNum)));
// 转换类型
Map<String, Product> prodMap = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparingInt(Product::getNum)), Optional::get)));
// 联合其他收集器
Map<String, Set<String>> prodMap = prodList.stream().collect(Collectors.groupingBy(Product::getCategory, Collectors.mapping(Product::getName, Collectors.toSet())));
说明
// 如果过滤为空,map转换也可以用,而且stream流不会产生null
List<String> commondList = strategyList.stream().filter(item -> "6".equals(item.getBasicElectricity())).map(BasicElectricity::getTime).collect(Collectors.toList());
}
日期时间
UTC(世界标准时间)
协调世界时,又称世界标准时间或世界协调时间,简称 UTC(从英文“Coordinated Universal Time”/法文“Temps Universel Coordonné”而来),是最主要的世界时间标准,其以原子时秒长为基础,在时刻上尽量接近于格林尼治标准时间。
GMT(格林尼治平均时间)
格林尼治平均时间(格林尼治标准时间),旧译格林威治标准时间;英语:Greenwich Mean Time,GMT)是指位于英国伦敦郊区的皇家格林尼治天文台的标准时间,因为本初子午线被定义在通过那里的经线。
理论上来说,格林尼治标准时间的正午是指当太阳横穿格林尼治子午线时(也就是在格林尼治上空最高点时)的时间。由于地球在它的椭圆轨道里的运动速度不均匀,这个时刻可能与实际的太阳时有误差,最大误差达 16 分钟。
由于地球每天的自转是有些不规则的,而且正在缓慢减速,因此格林尼治时间已经不再被作为标准时间使用。现在的标准时间,是由原子钟报时的协调世界时(UTC)。
CST(北京时间)
北京时间,China Standard Time,中国标准时间。在时区划分上,属东八区,比协调世界时早 8 小时,记为 UTC+8。
Java 默认使用的是 UTC 时间
新日期时间的优点
- 之前使用的 java.util.Date 类,月份是从 0 开始,所以处理时都会+ 1;而 java.time.LocalDate 月份和星期都改成了 enum;
- Date 和 SimpleDateFormat 都不是线程安全的;而 LocalDate 和 LocalTime 都是不可变类,不但线程安全,还不能修改;
- Date 包含日前、时间、毫秒数,需要根据需求取舍,而新 API 更好用的原因是考虑到了日期时间的操作,比如经常发生往前推或往后推几天的情况,用 Date 配合 Calendar 要写好多代码。
常用的类
- LocalDate
- LocalTime
- LocalDateTime
- DateTimeFormatter 格式化
- Instant 绝对时间,用于时间戳转换,在 Date 与 LocalDate、LocalDateTime 转换中,可以通过 Instant 作为中间类完成转换
- TemporalAdjuster 函数式接口,只提供了一个方法,用于调整 Temporal 对象的策略,在日期调整时非常有用;比如得到当月的第一天、最后一天,当年的第一天、最后一天,下一周或前一周的某天等。
- TemporalAdjusters TemporalAdjuster 对应的工具类,提供了现成的方法
- Period 表示一段时间的年月日,Period 基于日期值,而 Duration 基于时间值
- Duration 表示两个 Instant 间的一段时间
// 1. 时间戳
Instant now = Instant.now(); // 默认使用的是UTC时间Clock.systemUTC().instant(),与北京时间相差8个时区
Instant now = Instant.now().plusMillis(TimeUnit.HOURS.toMillis(8)); // 转换成北京时间
long seconds = now.getEpochSecond(); // 获取秒数(10位)
long timestamp = now.toEpochMilli(); // 获取毫秒数(13位)
LocalDateTime datetime = LocalDateTime.ofInstant(instant1, ZoneId.of("UTC+8")); // 转换成LocalDateTime对象
// 2. LocalDateTime
LocalDateTime datetime = LocalDateTime.now(); // LocalDate、LocalDateTime 的now()方法使用的是系统默认时区(UTC+8),也就是当前时间
Instant instant = datetime.atZone(ZoneId.systemDefault()).toInstant(); // 转换为UTC时间的Instant对象
Instant instant = datetime.atZone(ZoneId.of("UTC+8")).toInstant(); // 转换为当前时间的Instant对象
Instant instant = datetime.toInstant(ZoneOffset.ofHours(0)); // 转换为当前时间的Instant对象
// 3. Date 与LocalDateTime转换
Date date = new Date();
Instant instant = date.toInstant();
LocalDateTime datetime = LocalDateTime.ofInstant(instant, ZoneId.of("UTC+8")); // 转换成LocalDateTime对象
// 4. LocalDate、LocalTime方法和LocalDateTime差不多,包括创建、加、减、比较(之前、等于、之后)...
/* 1. now 相关的方法可以获取当前日期或时间
2. of 方法可以创建对应的日期或时间
3. parse 方法可以解析日期或时间
4. get 方法可以获取日期或时间信息
5. with 方法可以设置日期或时间信息
6. plus 或 minus 方法可以增减日期或时间信息*/
// 5. DateTimeFormatter 格式化
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime datetime = LocalDateTime.now();
String format = dateFormatter.format(datetime);
String datetime = "2011-01-01 23:23:12";
LocalDateTime parse = LocalDateTime.parse(datetime, dateFormatter);
var
概念
- 是 jdk10 的新特性,不是关键字,而是一种动态类型,只能用来定义局部变量,不能定义成员变量。
使用
- var 变量名 = 初始值
- new 对象().var + 快捷键,可以快速创建一个对象实例
java8新特性知识整理的更多相关文章
- java8 新特性精心整理
前言 越来越多的项目已经使用 Java 8 了,毫无疑问,Java 8 是Java自Java 5(发布于2004年)之后的最重要的版本.这个版本包含语言.编译器.库.工具和 JVM 等方面的十多个新特 ...
- java8 新特性精心整理(全)
前言 越来越多的项目已经使用 Java 8 了,毫无疑问,Java 8 是Java自Java 5(发布于2004年)之后的最重要的版本.这个版本包含语言.编译器.库.工具和 JVM 等方面的十多个新特 ...
- java8新特性视频、spring4.0视频讲解,javaee基础知识讲解等网址汇总
1.http://ke.atguigu.com/ 海量视频首页 2.http://ke.atguigu.com/course/56 java8新特性学习地址
- 2020你还不会Java8新特性?
Java8(1)新特性介绍及Lambda表达式 前言: 跟大娃一块看,把原来的电脑拿出来放中间看视频用 --- 以后会有的课程 难度 深入Java 8 难度1 并发与netty 难度3 JVM 难度4 ...
- 【Java8新特性】Stream API有哪些中间操作?看完你也可以吊打面试官!!
写在前面 在上一篇<[Java8新特性]面试官问我:Java8中创建Stream流有哪几种方式?>中,一名读者去面试被面试官暴虐!归根结底,那哥儿们还是对Java8的新特性不是很了解呀!那 ...
- java8新特性学习笔记
目录 1.速度更快 2.Lambda表达式 2.1.匿名内部类的Lambda转换 2.2.java8内置的四大核心函数式接口 2.3.方法引用和构造器 2.3.1.方法引用 2.3.2.构造器引用 2 ...
- java8新特性全面解析
在Java Code Geeks上有大量的关于Java 8 的教程了,像玩转Java 8--lambda与并发,Java 8 Date Time API 教程: LocalDateTime和在Java ...
- Java8 新特性之Stream----java.util.stream
这个包主要提供元素的streams函数操作,比如对collections的map,reduce. 例如: int sum = widgets.stream() .filter(b -> b.ge ...
- 这可能是史上最好的 Java8 新特性 Stream 流教程
本文翻译自 https://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/ 作者: @Winterbe 欢迎关注个人微信公众 ...
- Java8新特性之二:方法引用
上一节介绍了Java8新特性中的Lambda表达式,本小节继续讲解Java8的新特性之二:方法引用.方法引用其实也离不开Lambda表达式. 1.方法引用的使用场景 我们用Lambda表达式来实现匿名 ...
随机推荐
- .NET Core 在其上下文中,该请求的地址无效。
.NET Core 在其上下文中,该请求的地址无效. 看了端口,发现没被占用,后来发现是IP地址变了 改成正确的IP就可以了.
- ScreenToGif 录屏转git图片
ScreenToGif 一款开源的屏幕录制,允许您记录屏幕的选定区域.网络摄像头的实时信息或素描板上的实时绘图.之后,您可以编辑动画并将其保存为 gif.apng.视频.psd 或 png 图像. 官 ...
- JDk 与 ADB 环境变量配置
### Java环境变量配置 首先,JDK是整个Java的核心,包括了Java运行环境,一推Java工具和Java基础的类库. 网址:https://www.oracle.com/technetwor ...
- umount.nfs4: /home/videorec/sharedir: device is busy
用umount取消挂载时报错设备繁忙:device is busy.原因是还有进程在打开目录下的文件,可以先杀死进程,再卸载,或者强制卸载 umount 使用umount强制卸载,参数如下: -l ...
- Mysql--编译安装5.6版本
1 下载编译工具 yum -y install cmake gcc gcc-c++ ncurses-devel autoconf 2 创建用户 目录 useradd -s /sbin/nologin ...
- linux安装pyarmor踩坑记录
现有环境 centos 7.8 python 3.7.6 pip 20.0 找度娘学习安装pyarmor pip install pyarmor 然后查看版本 pyarmor --version 进入 ...
- 文心一言 VS 讯飞星火 VS chatgpt (182)-- 算法导论13.4 6题
六.用go语言,Skelton 和 Baron 教授担心在 RB-DELETE-FIXUP 的情况1开始时,结点 x.p 可能不是黑色的.如果这两位教授是对的,则第5~6行就是错的.证明:x.p 在情 ...
- uni-app 从入门到放弃(持续更新)
https://blog.csdn.net/weixin_33940102/article/details/91460204
- P5723 注意特殊情况
https://www.luogu.com.cn/problem/P5723 不是难题,但是倘若忽略L<2的情况就无法AC,Lougu得分只有80.因此写完题后一定要把各种边界性质的数据想出并用 ...
- freeswitch-1.10.7性能测试
概述 freeswitch 是一款简单好用的开源软交换平台. freeswitch-1.10.7是比较新的版本,使用时间比较短,需要一个可参考的性能指标,作为实际使用过程中的配置指导. 环境 cent ...