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的更多相关文章

  1. WIN10下java8的开发环境配置与第一个java程序

    一.开发环境配置 1.在官网上下载jdk-8u111-windows-x64.exe 2.运行安装包,可以自定义安装路径 3.进入环境变量设置: 计算机右键-->属性-->高级系统设置-- ...

  2. ubuntu下java8卸载

    要删除 OpenJDK (如果已安装的话).首先,检查是安装的哪个 OpenJDK包. # dpkg --list | grep -i jdk 移除 openjdk包: # apt-get purge ...

  3. position relative top失效的问题,温习下常用两种的居中方式

    因为body和html,默认高度是auto 所以相对于他们作为父元素设置position:relative的top值需要加上body,html{height:100%;} <!DOCTYPE h ...

  4. java8 学习系列--NIO学习笔记

    近期有点时间,决定学习下java8相关的内容: 当然了不止java8中新增的功能点,整个JDK都需要自己研究的,不过这是个漫长的过程吧,以自己的惰性来看: 不过开发中不是有时候讲究模块化开发么,那么我 ...

  5. C语言 在VS环境下一个很有意思的报错:stack around the variable was corrupted

    今天做一个很简单的oj来温习下c 语言 题目如下 输入 3位正整数 输出 逆置后的正整数 代码如下: #include"stdio.h"int main(){ float h,su ...

  6. 初探Java8中的HashMap(转)

    HashMap是我们最常用的集合之一,同时Java8也提升了HashMap的性能.本着学习的原则,在这探讨一下HashMap. 原理 简单讲解下HashMap的原理:HashMap基于Hash算法,我 ...

  7. 从壹开始微服务 [ DDD ] 之十二 ║ 核心篇【下】:事件驱动EDA 详解

    缘起 哈喽大家好,又是周二了,时间很快,我的第二个系列DDD领域驱动设计讲解已经接近尾声了,除了今天的时间驱动EDA(也有可能是两篇),然后就是下一篇的事件回溯,就剩下最后的权限验证了,然后就完结了, ...

  8. java8的新特性以及用法简介

    1. 介绍 2 接口的默认方法 2 lambda表达式 2.1 函数式接口 2.2 方法与构造函数引用 2.3 访问局部变量 2.4 访问对象字段与静态变量 3. 内建函数式接口 3.1 Predic ...

  9. Java8新特性--流(Stream)

    1.简介      Java 8是Java自Java 5(发布于2004年)之后的最重要的版本.这个版本包含语言.编译器.库.工具和JVM等方面的十多个新特性.在本文中我们一起来学习引入的一个新特性- ...

随机推荐

  1. AutoCAD .NET: 遍历模型空间

    原文:http://spiderinnet1.typepad.com/blog/2012/06/autocad-net-iterate-through-model-space.html https:/ ...

  2. VS引用文件出现黄色感叹号丢失文件,应该如何解决?

    VS是微软开发的一款超级强大的IDE,深受广大.net开发者喜爱. 但是再强大,也会有它的bug和缺点. 多人协同开发时,不知道你有没有遇到一个这样的情况:第二天上班,早早来到公司,打开电脑,拉取一下 ...

  3. vs中的system指令

    vs中的system指令 system(“命令语句”);必须要用到头文件include<stdio.h> system里可以加许多指令 取消关机   shutdown -a 关机   sh ...

  4. springbootl用thymeleaf整合htm

    pom文件: <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...

  5. 分布式事务TransactionScope所导致几个坑

    记录一下,个人见解,欢迎指正 错误: 1.该伙伴事务管理器已经禁止了它对远程/网络事务的支持. (异常来自 HRESULT:0x8004D025)2.事务已被隐式或显式提交,或已终止3.此操作对该事务 ...

  6. 内容协商在视图View上的应用【享学Spring MVC】

    每篇一句 人生很有意思:首先就得活得长.活得长才能够见自己,再长就可以见众生 前言 在经过 前两篇 文章了解了Spring MVC的内容协商机制之后,相信你已经能够熟练的运用Spring MVC提供的 ...

  7. 设计模式(C#)——07装饰者模式

    推荐阅读:  我的CSDN  我的博客园  QQ群:704621321       在一款战斗类的游戏中,随着故事情节的发展,玩家(即游戏中的主角,下文统一为主角)通常会解锁一些新技能.最初主角只有使 ...

  8. 业务代码的救星——Java 对象转换框架 MapStruct 妙用

    简介 在业务项目的开发中,我们经常需要将 Java 对象进行转换,比如从将外部微服务得到的对象转换为本域的业务对象 domain object,将 domain object 转为数据持久层的 dat ...

  9. Struts2:request & response

    整理自网上:  1.   获取Request和Response的方法  1.1. ServletActionContext的静态方法      HttpServletRequest request = ...

  10. HTML(七)CSS

    一个实例 <!DOCTYPE html> 菜鸟教程(runoob.com) body { background-color:#d0e4fe; } h1 { color:orange; te ...