JDK13,不如温习下Java8
JDK13于9月17号正式GA,版本新特性可参考: https://www.oschina.net/news/109934/jdk-13-released
虽然JDK更新迅速,但开发者貌似并不买账,据统计,目前仍以JDK8使用最多,预计可能还会延续好长一段时间。虽然JDK版本已至13,但对Java8的新特性,掌握程度如何呢?
本文对Java8的主要特性进行了梳理。供温习参考。
1. 接口默认方法
以前的接口只允许有抽象方法(没有实现体),java8中提供了接口默认方法支持,即可以提供方法的默认实现,实现类可以直接继承,也可以覆盖。默认方法主要解决接口的修改导致现有实现类不兼容的问题。
@RunWith(SpringRunner.class)
@SpringBootTest
public class InterfaceDefaultFunctionTest { public interface MyFunction<T> {
T func(T t); //默认方法
default int func2(T t){
return t.hashCode();
}
//静态方法
static<T> void print(T t) {
System.out.println(t);
}
} @Test
public void testInterface(){
MyFunction<String> myFunction = new MyFunction<String>(){
@Override
public String func(String s) {
return s.toUpperCase();
}
};
System.out.println(myFunction.func("abc"));
System.out.println(myFunction.func2("abc"));
LambdaTest.MyFunction.print("efg");
}
}
默认方法通过关键字 default 声明。同时也可以在接口中定义静态方法。
2. 函数式接口
函数式接口就是有且仅有一个抽象方法的接口(可以有其它非抽象方法),如1所示代码中 MyFunction 就是一个函数式接口,只有一个抽象方法 func, 其它非抽象方法如默认方法 func2, 静态方法 print 不影响其函数式接口的特性。
函数式接口可以使用注解 @FunctionalInterface 标注,该注解会去检查接口是否符合函数式接口的规范要求,不符合的话IDE会给出提示。
java中内置了一些函数式接口,
函数式接口 | 描述 |
---|---|
Consumer | 包含方法 void accept(T t), 对类型为T的对象t进行操作 |
Supplier | 包含方法 T get(),返回类型为T的对象 |
Function<T,R> | 包含方法 R apply(T t),对类型为T的对象进行操作,返回类型R的对象 |
Predicat | 包含方法 boolean test(T t), 判断类型为T的对象是否满足条件 |
以及基于这些接口的其它变种或子接口,如BiConsumer<T,U>,BiFunction<T,U,R>等。还有如Runnable,Callable等接口,也属于函数式接口 —— 都只有一个抽象方法。
@FunctionalInterface
public interface BiConsumer<T, U> {
void accept(T t, U u); default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) {
Objects.requireNonNull(after); return (l, r) -> {
accept(l, r);
after.accept(l, r);
};
}
}
3. Lambda表达式
lambda表达式实质就是一个匿名函数,在python中很常见,java到了jdk8提供了支持。
lambda表达式的格式形如: (参数) -> {方法体语句},当参数只有一个时,左边小括号可以省略,当方法体语句只有一条时,右边大括号可以省略。
Java的lambda表达式基本上是对函数式接口实现的一种简化 —— 用lambda表达式直接代替一个函数式接口的具体实现(抽象方法的实现)。当我们使用jdk8在IDE中编写1中代码时,IDE会给出提示,
匿名实现类可以用lambda表达式替换。上述代码使用lambda表达式替换可调整为,
@Test
public void testInterface(){
MyFunction<String> myFunction = s -> s.toUpperCase();
System.out.println(myFunction.func("abc"));
System.out.println(myFunction.func2("abc"));
}
lambda表达式甚至可作为方法参数传入(实质也是作为一个函数式接口的实现类实例)
@FunctionalInterface
public interface MyFunction<T> {
T func(T t);
} public void print(MyFunction<String> function, String s){
System.out.println(function.func(s));
} @Test
public void testInterface(){
//将lambda表达式作为方法参数传入
print((String s) -> s.toUpperCase(), "abc");
}
局部变量在lambda表达式中是只读的,虽可不声明为final,但无法修改。如
@Test
public void testInterface(){
int i = 1;
//lambda表达式中无法修改局部变量i,将报编译错误
print((String s) -> {i = i+10; return s.toUpperCase();}, "abc");
}
4. 方法引用
当需要使用lambda表达式时,如果已经有了相同的实现方法,则可以使用方法引用来替代lambda表达式,几种场景示例如下。
@RunWith(SpringRunner.class)
@SpringBootTest
public class FunctionReferenceTest { @Test
public void testFunctionReference() {
// 实例::实例方法
Consumer<String> consumer = s -> System.out.println(s); //lambda表达式
Consumer<String> consumer2 = System.out::println; //方法引用
consumer.accept("abc");
consumer2.accept("abc"); //类::静态方法
Comparator<Integer> comparator = (x, y) -> Integer.compare(x, y); //lambda表达式
Comparator<Integer> comparator2 = Integer::compare; //方法引用
System.out.println(comparator.compare(10, 8));
System.out.println(comparator2.compare(10, 8)); //类::实例方法, 当引用方法是形如 a.func(b)时,用类::实例方法的形式
BiPredicate<String, String> biPredicate = (a, b) -> a.equals(b); //lambda表达式
BiPredicate<String, String> biPredicate2 = String::equals; //方法引用
System.out.println(biPredicate.test("abc", "abb"));
System.out.println(biPredicate2.test("abc","abb")); //type[]::new 数组引用
Function<Integer,Integer[]> fun= n-> new Integer[n]; //lambda表达式
Function<Integer,Integer[]> fun2=Integer[]::new; //方法引用
System.out.println(fun.apply(10));
System.out.println(fun2.apply(10)); //构造器引用
Function<String,String> func = n-> new String(n); //lambda表达式
Function<String,String> func2 = String::new; //方法引用
System.out.println(func.apply("aaa"));
System.out.println(func2.apply("aaa"));
}
}
5. Stream API
Stream与lambda应该是java8最重要的两大特性。Stream 对集合的处理进行了抽象,可以对集合进行非常复杂的查找、过滤和映射等操作。提供了一种高效的且易于使用的处理数据的方式。
Stream的三个特性:
- Stream本身不会存储元素
- Stream不会改变操作对象(即集合),会返回一个新的Stream
- Stream的中间操作不会立刻执行,而是会等到需要结果的时候才执行
Java8 的Collection接口包含了两个方法 stream(), parallelStream(), 分别返回一个顺序流与一个并行流,所有Collection类型(如List, )的对象可以调用这两个方法生成流。
Java8 的Arrays类也提供了 stream(T[] array)等方法用以生成流。也可以使用静态方法 Stream.iterate() 和 Stream.generate() 来创建无限流。
Stream的中间操作包括
操作 | 描述 |
---|---|
filter(Predicate p) | 接收 Lambda , 从流中过滤出满足条件的元素 |
distinct() | 通过hashCode() 和 equals() 去除重复元素 |
limit(long maxSize) | 截断流,使元素的个数不超过给定数量 |
skip(long n) | 跳过前面的n个元素,若流中元素不足n个,则返回一个空流 |
map(Function f) | 将每个元素使用函数f执行,将其映射成一个新的元素 |
mapToDouble(ToDoubleFunction f) | 将每个元素使用f执行,产生一个新的DoubleStream流 |
mapToInt(ToIntFunction f) | 将每个元素使用f执行,产生一个新的IntStream流 |
mapToLong(ToLongFunction f) | 将每个元素使用f执行,产生一个新的LongStream流 |
flatMap(Function f) | 将流中的每个值都通过f转换成另一个流,然后把所有流连接成一个流 |
sorted() | 按自然顺序排序,产生一个新流 |
sorted(Comparator comp) | 根据比较器排序,产生一个新流 |
allMatch(Predicate p) | 判断是否匹配所有元素 |
anyMatch(Predicate p) | 判断是否匹配至少一个元素 |
noneMatch(Predicate p) | 判断是否没有匹配任意元素 |
findFirst() | 返回第一个元素 |
findAny() | 返回任意一个元素 |
reduce(T iden, BinaryOperator b) | 对流中的元素进行reduce操作,返回T类型对象 |
reduce(BinaryOperator b) | 对流中的元素进行reduce操作,返回Optional对象 |
Stream的终止操作包括
操作 | 描述 |
---|---|
count() | 返回元素总数 |
max(Comparator c) | 返回最大值 |
min(Comparator c) | 返回最小值 |
forEach(Consumer c) | 内部迭代调用Consumer操作 |
collect(Collector c) | 将流转换为其他形式,一般通过Collectors来实现 |
Stream使用示例
@Test
public void testStream() {
List<User> list = new ArrayList<>();
//转换为List,这里没啥意义,仅做示范
List<User> users = list.stream().collect(Collectors.toList());
//转换为Set
Set<User> users1 = list.stream().collect(Collectors.toSet());
//转换为Collection
Collection<User> users2 = list.stream().collect(Collectors.toCollection(ArrayList::new));
//计数
long count = list.stream().collect(Collectors.counting());
//求和
int total = list.stream().collect(Collectors.summingInt(User::getAge));
//求平均值
double avg= list.stream().collect(Collectors.averagingInt(User::getAge));
//获取统计对象,通过该统计对象可获取最大值,最小值之类的数据
IntSummaryStatistics iss= list.stream().collect(Collectors.summarizingInt(User::getAge));
//将值通过","拼接
String str= list.stream().map(User::getName).collect(Collectors.joining(","));
//最大值
Optional<User> max= list.stream().collect(Collectors.maxBy(Comparator.comparingInt(User::getAge)));
//最小值
Optional<User> min = list.stream().collect(Collectors.minBy(Comparator.comparingInt(User::getAge)));
//从累加器开始,对指定的值,这里是年龄,进行sum的reduce操作
int t =list.stream().collect(Collectors.reducing(0, User::getAge, Integer::sum));
//对转换的结果再进行处理
int how = list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
//分组
Map<String, List<User>> map= list.stream().collect(Collectors.groupingBy(User::getName));
//根据条件进行分区
Map<Boolean,List<User>> vd= list.stream().collect(Collectors.partitioningBy(u -> u.getName().startsWith("W"))); }
6. Optional类
Optional是一个容器类,可以避免显式的null判断,基本使用示例如下
@RunWith(SpringRunner.class)
@SpringBootTest
public class OptionalTest { @Test
public void testOptional(){
// of 不允许传入null值,否则抛出NPE
Optional<Integer> optional = Optional.of(new Integer(10));
System.out.println(optional.get()); // ofNullable 允许传入null,但是直接调用get会抛出NoSuchElementException异常,
// 可通过isPresent判断是否存在值
Optional<Integer> optional1 = Optional.ofNullable(null);
if(optional1.isPresent()) {
System.out.println(optional1.get());
}else{
System.out.println("optional1 is empty");
}
// orElse 判断是否存在值,存在则返回,不存在则返回参数里的值
Integer value = optional1.orElse(new Integer(0)); // map方法,如果optional有值,则对值进行处理返回新的Optional,
// 如果没有值则返回Optional.empty()
optional = optional.map(x -> x*x);
System.out.println(optional.get()); // 与map类似,只是要求返回值必须是Optional,进一步避免空指针
optional = optional.flatMap(x ->Optional.of(x*x));
System.out.println(optional.get()); }
}
7. Base64
在java8中,Base64成为了java类库的标准,可直接使用
import java.util.Base64; @RunWith(SpringRunner.class)
@SpringBootTest
public class Base64Test { @Test
public void testBase64(){
//base64编码
String encode = Base64.getEncoder().encodeToString("abc".getBytes());
System.out.println(encode);
//base64解码
System.out.println(new String(Base64.getDecoder().decode(encode)));
}
}
8. 日期时间类
以前的Date类是非线程安全的,并且一些常用的日期时间运算需要自己编写util工具类。java8推出了java.time包,里面包含了如 LocalDate, LocalTime, LocalDateTime等类,可方便地进行日期时间的运算,如日期间隔、时间间隔,日期时间的加减,格式化等等。
—————————————————————————————
作者:空山新雨
欢迎关注我的微信公众号:jboost-ksxy
JDK13,不如温习下Java8的更多相关文章
- WIN10下java8的开发环境配置与第一个java程序
一.开发环境配置 1.在官网上下载jdk-8u111-windows-x64.exe 2.运行安装包,可以自定义安装路径 3.进入环境变量设置: 计算机右键-->属性-->高级系统设置-- ...
- ubuntu下java8卸载
要删除 OpenJDK (如果已安装的话).首先,检查是安装的哪个 OpenJDK包. # dpkg --list | grep -i jdk 移除 openjdk包: # apt-get purge ...
- position relative top失效的问题,温习下常用两种的居中方式
因为body和html,默认高度是auto 所以相对于他们作为父元素设置position:relative的top值需要加上body,html{height:100%;} <!DOCTYPE h ...
- java8 学习系列--NIO学习笔记
近期有点时间,决定学习下java8相关的内容: 当然了不止java8中新增的功能点,整个JDK都需要自己研究的,不过这是个漫长的过程吧,以自己的惰性来看: 不过开发中不是有时候讲究模块化开发么,那么我 ...
- C语言 在VS环境下一个很有意思的报错:stack around the variable was corrupted
今天做一个很简单的oj来温习下c 语言 题目如下 输入 3位正整数 输出 逆置后的正整数 代码如下: #include"stdio.h"int main(){ float h,su ...
- 初探Java8中的HashMap(转)
HashMap是我们最常用的集合之一,同时Java8也提升了HashMap的性能.本着学习的原则,在这探讨一下HashMap. 原理 简单讲解下HashMap的原理:HashMap基于Hash算法,我 ...
- 从壹开始微服务 [ DDD ] 之十二 ║ 核心篇【下】:事件驱动EDA 详解
缘起 哈喽大家好,又是周二了,时间很快,我的第二个系列DDD领域驱动设计讲解已经接近尾声了,除了今天的时间驱动EDA(也有可能是两篇),然后就是下一篇的事件回溯,就剩下最后的权限验证了,然后就完结了, ...
- java8的新特性以及用法简介
1. 介绍 2 接口的默认方法 2 lambda表达式 2.1 函数式接口 2.2 方法与构造函数引用 2.3 访问局部变量 2.4 访问对象字段与静态变量 3. 内建函数式接口 3.1 Predic ...
- Java8新特性--流(Stream)
1.简介 Java 8是Java自Java 5(发布于2004年)之后的最重要的版本.这个版本包含语言.编译器.库.工具和JVM等方面的十多个新特性.在本文中我们一起来学习引入的一个新特性- ...
随机推荐
- AutoCAD .NET: 遍历模型空间
原文:http://spiderinnet1.typepad.com/blog/2012/06/autocad-net-iterate-through-model-space.html https:/ ...
- VS引用文件出现黄色感叹号丢失文件,应该如何解决?
VS是微软开发的一款超级强大的IDE,深受广大.net开发者喜爱. 但是再强大,也会有它的bug和缺点. 多人协同开发时,不知道你有没有遇到一个这样的情况:第二天上班,早早来到公司,打开电脑,拉取一下 ...
- vs中的system指令
vs中的system指令 system(“命令语句”);必须要用到头文件include<stdio.h> system里可以加许多指令 取消关机 shutdown -a 关机 sh ...
- springbootl用thymeleaf整合htm
pom文件: <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...
- 分布式事务TransactionScope所导致几个坑
记录一下,个人见解,欢迎指正 错误: 1.该伙伴事务管理器已经禁止了它对远程/网络事务的支持. (异常来自 HRESULT:0x8004D025)2.事务已被隐式或显式提交,或已终止3.此操作对该事务 ...
- 内容协商在视图View上的应用【享学Spring MVC】
每篇一句 人生很有意思:首先就得活得长.活得长才能够见自己,再长就可以见众生 前言 在经过 前两篇 文章了解了Spring MVC的内容协商机制之后,相信你已经能够熟练的运用Spring MVC提供的 ...
- 设计模式(C#)——07装饰者模式
推荐阅读: 我的CSDN 我的博客园 QQ群:704621321 在一款战斗类的游戏中,随着故事情节的发展,玩家(即游戏中的主角,下文统一为主角)通常会解锁一些新技能.最初主角只有使 ...
- 业务代码的救星——Java 对象转换框架 MapStruct 妙用
简介 在业务项目的开发中,我们经常需要将 Java 对象进行转换,比如从将外部微服务得到的对象转换为本域的业务对象 domain object,将 domain object 转为数据持久层的 dat ...
- Struts2:request & response
整理自网上: 1. 获取Request和Response的方法 1.1. ServletActionContext的静态方法 HttpServletRequest request = ...
- HTML(七)CSS
一个实例 <!DOCTYPE html> 菜鸟教程(runoob.com) body { background-color:#d0e4fe; } h1 { color:orange; te ...