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> 输入一个值,返回一个布尔
闭包??? java中的体现,内部类,闭包能够将一个方法作为一个变量去存储,这个方法有能力去访问所在类的自由变量。
就是一个方法。入参->出参。关键在于,lambda可以直接作为方法参数。也就是一个方法【一定有返回值的方法】作为另一个方法的参数。
以下是lambda表达式的重要特征: ()-> { }
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里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。
参考:
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
从 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.Decoder和Encoder
原文:https://www.cnblogs.com/wenhui2015/p/14532202.html