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. cs231n---生成模型

    1 生成模型的定义和分类 生成模型是一种无监督学习方法.其定义是给一堆由真实分布产生的训练数据,我们的模型从中学习,然后以近似于真实的分布来产生新样本. 生成模型分为显式和隐式的生成模型: 为什么生成 ...

  2. EXP查询合集提权后渗透必备

    0x00 整理的一些后渗透提权需要用到的一些漏洞,后渗透提权的时候可以看一下目标机那些补丁没打,再进行下一步渗透提权. 0x01 CVE-2019-0803 [An elevation of priv ...

  3. jenkins无法连接到git原因

    1.账号密码错误 2.公钥私钥不对应(git上为公钥,jenkins为私钥,私钥比公钥长) 3.公钥私钥文件没有复制到jenkins目录下的.ssh文件中

  4. Day 02--选题与设计(二)

    1.今天我们主要设计了一下我们微信小程序可以实现的功能,客户操作的基本流程,研究了墨刀这个工具的使用方法并试着将想法转化为原型设计项目.我们给自己的系统起名为“天天好餐”.我们认为食堂订送餐与网络上的 ...

  5. Go-json解码到结构体-踩坑

    package main import ( "encoding/json" "fmt" ) type User struct { Name string `js ...

  6. net core Webapi基础工程搭建(一)——开发工具及环境

    目录 开发工具 版本 后端框架 开发工具 Visual Studio 2019,既然要折腾那就体验最新版的开发工具有什么特殊的地方,之前个人开发使用的是2017. 下载地址:https://visua ...

  7. docker安装到基本使用

    记录docker概念,安装及入门日常使用 Docker安装(Linux / Debian) 查看官方文档,在Debian上安装Docker,其他平台在这里查阅,以下均在root用户下操作,省去sudo ...

  8. springboot中的springSession的存储和获取

    利用redis进行springSession的存储: 存储: // 在session中保存用户信息 HttpSession session = httpRequest.getSession(true) ...

  9. SSH开发模式——Struts2(第二小节)

    上一小节已经学会了如何去搭建Struts2的开发环境,该篇博客我们继续深入Struts2,了解Struts2框架的拦截器. 首先对我们在web.xml文件配置的过滤器进行一个源码的分析. 在Strut ...

  10. Nginx + fastcgi + php 的原理与关系

    CGI:Common Gateway Interface 公共网关接口,web服务器和脚本语言通信的一个标准.接口.协议[协议] FastCGI:CGI协议的升级版[协议] PHP-CGI: 实现了C ...