java8最重要的是实现函数式编程。即方法本身就是一种数据类型,可作为参数传入。
函数式接口
只有一个抽象方法的接口,就叫函数式接口。实现类只需要实现这一个方法即可。只需要实现一个方法,可以写成lambdal表达式。(以往,是可以使用匿名内部类,但匿名内部类的不同之处,要实现接口的所有抽象方法,因此匿名内部类不完全可以用lambdal表达式来代替)
public class TestLambda { public static void main(String[] args) { Runnable task = () -> {for (int i = 0; i < 10; i++) { System.out.println("it is :" + Thread.currentThread().getName()); }}; Thread thread = new Thread(task); thread.start(); for (int i = 0; i < 10; i++) { System.out.println("it is :" + Thread.currentThread().getName()); } } }
以上这种写法,函数式编程。
JDK 1.8 新增加的函数接口:java.util.function,包含了很多类,用来支持 Java的 函数式编程;
Function<T,R> 输入T,返回R
Consumer<T> 输入一个值,没有返回值
Supplier<T> 没有输入值,返回一个值
Predicate<T> 输入一个值,返回一个布尔
Lambda表达式
闭包??? java中的体现,内部类,闭包能够将一个方法作为一个变量去存储,这个方法有能力去访问所在类的*变量。
就是一个方法。入参->出参。关键在于,lambda可以直接作为方法参数。也就是一个方法【一定有返回值的方法】作为另一个方法的参数。
以下是lambda表达式的重要特征: ()-> { }
- 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
- 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号【例如stream对map进行遍历时】。 如果是空参也可以使用()
- 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。 -> {}
- 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。如果有返回值,就要return进行声明
Lambda 表达式免去了使用匿名方法的麻烦,并且给予Java简单但是强大的函数化的编程能力。
lambda 表达式只能引用标记了 final 的外层局部变量,这就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。
public class Java8Tester { public static void main(String args[]) { final int num = 1; Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num)); s.convert(2); // 输出结果为 3 } public interface Converter<T1, T2> { void convert(int i); } }
说明:
Lambda 表达式的局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有 final 的语义), 匿名内部类也是如此;
在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量;
跟匿名内部类很像,但范围要小,只能实现函数式接口;
能写成lambda表达式的,都是对于函数式接口,写的都是其实现类,例如stream的操作,方法里写的lamdal都是函数式接口的实现类;
局部的,不能用static修饰,可以调用局部变量(隐式final)和成员变量(无论实例或者静态)
方法引用和构造器引用
::两个英文冒号
当lambda表达式,代码块只有一行代码,不仅可以省略花括号,还可以写成方法引用。
默认方法
Java 8 新增了接口的默认方法。
简单说,默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法。
我们只需在方法名前面加个default关键字即可实现默认方法。 不能加static【就是给实现类用的,不是接口本身用的】,不能private【否则不能继承了】
为什么要有这个特性? 如果修改接口就要修改所有的实现类;所以直接将要接口要修改的部分,即增加的功能,直接在接口进行实现,实现类不需要修改进行实现。此时这个接口类似抽象类了,不仅有抽象方法也有非抽象方法。
首先,之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类【接口定义好规范之后不能随便再修改,否则所有的实现类都要进行修改】,目前的java 8之前的集合框架没有foreach方法,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。
流式编程stream
参考:
https://blog.csdn.net/qq_20989105/article/details/81234175
Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。
Stream API 借助于同样新出现的Lambda表达式,极大的提高编程效率和程序可读性。
同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用fork/join并行方式来拆分任务和加速处理过程。
通常编写并行代码很难而且容易出错, 但使用Stream API无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序。
所以说,Java 8 中首次出现的java.util.stream 是一个函数式语言+多核时代综合影响的产物。
什么是流【其实是迭代器】
Stream 不是集合元素,它不是数据结构并不保存数据【集合是用来保存数据,是一个容器,是有数据结构的,数组,链表,队列等等】,它是有关算法和计算的,它更像一个高级版本的 Iterator。原始版本的Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换。【隐性的迭代器,流的形式】
Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。
而和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。顾名思义,当使用串行方式去遍历时,每个 item 读完后再读下一个 item。而使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。Stream 的并行操作依赖于 Java7 中引入的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程。
构造:
因为是迭代器,所以针对的是集合和数组而言,集合和数组都可以构造stream流;
类型:
对于基本数据类型,有三种对应的包装类型Stream,IntStream、LongStream、DoubleStream。当然我们也可以用 Stream<Integer>、Stream<Long> >、Stream<Double>,但是boxing和 unboxing会很耗时,所以特别为这三种基本数值型提供了对应的 Stream;
转换:
流可以转换成数据结构,集合
常见操作:
Intermediate:
map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered
Terminal:
forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator
Short-circuiting:
anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit
Optional 类
从 Java 8 引入的一个很有趣的特性是 Optional 类。Optional 类主要解决的问题是臭名昭著的空指针异常(NullPointerException) NEP
本质上,这是一个包含有可选值的包装类,这意味着 Optional 类既可以含有对象也可以为空。
Optional 是 Java 实现函数式编程的强劲一步,并且帮助在范式中实现。但是 Optional 的意义显然不止于此。
在 Java 8 之前,任何访问对象方法或属性的调用都可能导致 NullPointerException:
String isocode = user.getAddress().getCountry().getIsocode().toUpperCase();
在这个小示例中,如果我们需要确保不触发异常,就得在访问每一个值之前对其进行明确地检查:
if (user != null) { Address address = user.getAddress(); if (address != null) { Country country = address.getCountry(); if (country != null) { String isocode = country.getIsocode(); if (isocode != null) { isocode = isocode.toUpperCase(); } } } }
变得冗长,难以维护。
String result = Optional.ofNullable(user) .flatMap(User::getAddress) .flatMap(Address::getCountry) .map(Country::getIsocode) .orElse("default");
构建
使用 of() 和 ofNullable() 方法创建包含值的 Optional;明确对象不为 null 的时候使用 of()。
如果对象即可能是 null 也可能是非 null,你就应该使用 ofNullable() 方法:
获取
get,可能会空,所以获取前判断使用ifPresent,此外如果存在可以lamdal对元素进行操作
Optional<ExamProduct> mayExists = this.findByVidAndProductCode(examProduct.getVid(), examProduct.getProductCode()); mayExists.ifPresent(product -> examProduct.setId(product.getId()));
默认值
如果为空时,就是返默认值。
orElse(),它的工作方式非常直接,如果有值则返回该值,否则返回传递给它的参数值;
orElseGet() —— 其行为略有不同。这个方法会在有值的时候返回值,如果没有值,它会执行作为参数传入的 Supplier(供应者) 函数式接口,并将返回其执行结果;
除了 orElse() 和 orElseGet() 方法,Optional 还定义了 orElseThrow() API —— 它会在对象为空的时候抛出异常,而不是返回备选的值;
时间类
之前的问题:Date非线程安全,可变;时区问题麻烦
Base64
引入作为标准类库
Base64.Decoder和Encoder