从SpringBoot 2.0.0开始,对于jdk的版本要求从原来的1.7改至1.8,很多人可能对这些新特性完全没有概念。那么对于2014年发布的jdk1.8的特性,你了解多少呢?在实际的编码中,你会想起用到的特性又有哪些?他究竟有哪些优势呢?请跟随我一起揭开它神秘的面纱,(#^.^#)
1.特点简介
- 速度更快
- 代码更少(增加了新的语法 Lambda 表达式)
- 强大的 Stream API
- 便于并行
- 最大化减少空指针异常 Optional
- Nashorm引擎,允许在JVM上运行JS应用
其中最为核心的为 Lambda 表达式与Stream API
2.Lambda表达式
Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
2.1示例
匿名内部类方式:
Comparator<Integer> comparable = new Comparator<Integer>() {
@Override
public int compare(Integer x, Integer y) {
return Integer.compare(x, y);
}
};
lambda表达式:
Comparator<Integer> comparable = (x, y) -> Integer.compare(x, y);
## 如果多个参数的顺序与执行的顺序一致,并且只有一条语句,可以进一步简化
====>
Comparator<Integer> comparable = Integer::compare;
总结:lambda的本质:作为函数式接口的实例
左边:
- 参数类型可以省略(lambda表达式运行依赖于上下文环境,是由编译器自动推断的,就是所谓的类型推断)
- 单个参数可以省略 (),如果只有一句执行语句,可以都省略
2.函数式接口
- 如果一个接口中,只拥有一个抽象方法,那么就是一个函数式接口。我们也可以自定义接口实现。
- 在任何接口上使用@FunctionalInterface 注解作为检查是否符合规范使用。
如果lambda表达式抛出一个受检异常,那么需要在接口上声明这个异常。 ```java @FunctionalInterface //作为检查是否符合规范使用 public interface MyFunction2
{ public R getValue(T t1, T t2);
}
@Test
public void test3(){
op(100L, 200L, (x, y) -> x + y);
op(100L, 200L, (x, y) -> x * y);
}
//需求:对于两个 Long 型数据进行处理
public void op(Long l1, Long l2, MyFunction2<Long, Long> mf){
System.out.println(mf.getValue(l1, l2));
}
<a name="nhPv9"></a>
## 2.1JAVA内置四大接口函数

<a name="zlFxV"></a>
# 3.方法引用和构造器引用
<a name="l3X7A"></a>
## 3.1方法引用
当要传递给Lambda体的操作,已经有实现的方法了,可以用方法引用。注意:实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致,包括顺序。<br />主要有以下三种使用情况:
- 对象::实例方法
- 类::静态方法
- 类::实例方法
**_类::静态方法_**
```java
MyFunction001 myFunction001 = str -> System.out.println(str);
转化为=====>
MyFunction001 myFunction001 = System.out::println;
=====================================
如果是以下形式,则不能使用
MyFunction001 myFunction001 = str -> System.out.println(str+"");
对象::实例方法
Comparator<Integer> comparable = (x, y) -> Integer.compare(x, y);
int compare = comparable.compare(6, 4);
转化为=====>
Comparator<Integer> comparable = Integer::compare;
int compare = comparable.compare(6, 4);
类::实例方法
注意:当需要引用方法的第一个参数是调用对象,并且第二个参数是需要引
用方法的第二个参数(或无参数)时:ClassName::methodName
MyFunction001 myFunction001 = (str1, str2) -> str1.equals(str2);
myFunction001.test("aa","bb");
转化为=====>
//标记为String是因为类型推断为String类
MyFunction001 myFunction001 = String::equals;
myFunction001.test("aa","bb");
3.2构造器引用
Supplier<Employee> supplier = new Supplier<Employee>() {
@Override
public Employee get() {
return new Employee();
}
};
或者
MyFunction001<Employee> supplier = (name, age) -> new Employee(name, age);
均可以转化为=====>
Supplier<Employee> supplier = Employee::new;
3.3数组引用
格式:type[]::new
Function<Integer,Integer[]> function = new Function<Integer, Integer[]>() {
@Override
public Integer[] apply(Integer integer) {
return new Integer[integer];
}
};
转化==>
Function<Integer,Integer[]> function = integer -> new Integer[integer];
转化==>
Function<Integer,Integer[]> function = Integer[]::new;
4.Stream API
4.1简介
Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API(java.util.stream.*)。Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
目的是写出高效、简洁的代码。
为什么使用 Stream API?
实际项目过程中,除了多数来自于Mysql等关系型数据库的数据以外,还会使用redis、MongDB等NoSql数据库。而这些数据需要Java来进行处理。
Stream和Collection集合的区别:
Collection是一种静态的内存数据结构,而Stream是有关计算的。前者面向内存进行存储,而后者面向CPU进行计算。
流(Stream) 到底是什么呢?
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算!”
注意:
①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
4.2Stream 操作三个步骤
创建Stream:
一个数据源(如:集合或者数组)
中间操作:
一个中间操作链,对上游数据进行处理然后交给下游。
终止操作(终端操作)
一旦执行终止操作,就会执行中间操作链得到结果,操作完成后就结束。
4.3Stream创建方式
//方式一:通过集合
List<Employee> emps = Arrays.asList(
new Employee(101, "张三", 18, 9999.99),
new Employee(102, "李四", 59, 6666.66),
new Employee(103, "王五", 28, 3333.33),
new Employee(104, "赵六", 8, 7777.77),
new Employee(105, "田七", 38, 5555.55)
);
//返回一个顺序流
Stream<Employee> stream = emps.stream();
//返回一个并行流
Stream<Employee> employeeStream = emps.parallelStream();
//方式二:通过数组
int[] arr = new int[]{1, 2, 3, 1, 4};
IntStream stream1 = Arrays.stream(arr);
//方式三:通过 of 方法
Stream<Object> stream2 = Stream.of(1, 2, 3, 4, 5, "66");
//方式四:创建无限流,自定义一些数据
Stream.iterate(0, integer -> integer + 2)
.limit(10)
.forEach(System.out::println);
4.3Stream的中间操作
- 筛选和切片
多个中间操作可以连接起来形成流水链,只有触发了终止操作,中间操作一次性处理完成,称为“惰性求值”。
filter——接收 Lambda , 从流中排除某些元素。
limit——截断流,使其元素不超过给定数量。
skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
distinct——筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
Stream API 和 传统遍历操作区别
//内部迭代:迭代操作 Stream API 内部完成
@Test
public void test2(){
//所有的中间操作不会做任何的处理
Stream<Employee> stream = emps.stream()
.filter((e) -> {
System.out.println("测试中间操作");
return e.getAge() <= 35;
});
//只有当做终止操作时,所有的中间操作会一次性的全部执行,称为“惰性求值”
stream.forEach(System.out::println);
}
//外部迭代
@Test
public void test3(){
Iterator<Employee> it = emps.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
映射
方法 | 描述
-|-
map(Function f) | 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 |
mapToDouble(ToDoubleFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。 |
mapToInt(ToIntFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。 |
mapToLong(ToLongFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。|
flatMap(Function f) | 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。|
排序
sorted():产生一个新流,按照自然排序。
sorted(Comparator comp):产生一个新流,按照比较器排序。
emps.stream()
.sorted((x, y) -> {
if(x.getAge() == y.getAge()){
return x.getName().compareTo(y.getName());
}else{
return Integer.compare(x.getAge(), y.getAge());
}
}).forEach(System.out::println);
4.3Stream的终止操作
终端操作会从流的流水线中生成结果,结果可以是任何不是流的值,例如 list、set、void。
查找与匹配
- allMatch——检查是否匹配所有元素
- anyMatch——检查是否至少匹配一个元素
- noneMatch——检查是否没有匹配的元素
- findFirst——返回第一个元素
- findAny——返回当前流中的任意元素
- count——返回流中元素的总个数
- max——返回流中最大值
- min——返回流中最小值
- forEach——内部迭代
注意:流进行了终止操作后,不能再进行操作了
@Test
public void test1(){
boolean bl = emps.stream()
.allMatch((e) -> e.getStatus().equals(Status.BUSY));
System.out.println(bl);
boolean bl1 = emps.stream()
.anyMatch((e) -> e.getStatus().equals(Status.BUSY));
System.out.println(bl1);
boolean bl2 = emps.stream()
.noneMatch((e) -> e.getStatus().equals(Status.BUSY));
System.out.println(bl2);
}
@Test
public void test2(){
Optional<Employee> op = emps.stream()
.sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
.findFirst();
System.out.println(op.get());
System.out.println("--------------------------------");
Optional<Employee> op2 = emps.parallelStream()
.filter((e) -> e.getStatus().equals(Status.FREE))
.findAny();
System.out.println(op2.get());
}
//注意:流进行了终止操作后,不能再次使用
@Test
public void test4(){
Stream<Employee> stream = emps.stream()
.filter((e) -> e.getStatus().equals(Status.FREE));
long count = stream.count();
//下面代码无效,会报错
stream.map(Employee::getSalary)
.max(Double::compare);
}
归约
reduce(T iden,BinaryOperator b)——可以将流的元素反复结合起来,得到一个值,返回 T。
reduce(BinaryOperator b)——可以将流的元素反复结合起来,得到一个值,返回 Optional
@Test
public void test1(){
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum = list.stream()
.reduce(0, (x, y) -> x + y);
System.out.println(sum);
System.out.println("----------------------------------------");
Optional<Double> op = emps.stream()
.map(Employee::getSalary)
.reduce(Double::sum);
System.out.println(op.get());
}
收集
collect(Collecttor c)——接收一个Collection接口的实现,用于给Stream做元素汇总方法。
@Test
public void test4(){
Optional<Double> max = emps.stream()
.map(Employee::getSalary)
.collect(Collectors.maxBy(Double::compare));
System.out.println(max.get());
Optional<Employee> op = emps.stream()
.collect(Collectors.minBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
System.out.println(op.get());
Double sum = emps.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(sum);
Double avg = emps.stream()
.collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println(avg);
Long count = emps.stream()
.collect(Collectors.counting());
System.out.println(count);
System.out.println("--------------------------------------------");
DoubleSummaryStatistics dss = emps.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(dss.getMax());
}
//分组
@Test
public void test5(){
Map<Status, List<Employee>> map = emps.stream()
.collect(Collectors.groupingBy(Employee::getStatus));
System.out.println(map);
}
//多级分组
@Test
public void test6(){
Map<Status, Map<String, List<Employee>>> map = emps.stream()
.collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
if(e.getAge() >= 60)
return "老年";
else if(e.getAge() >= 35)
return "中年";
else
return "成年";
})));
System.out.println(map);
}
//分区
@Test
public void test7(){
Map<Boolean, List<Employee>> map = emps.stream()
.collect(Collectors.partitioningBy((e) -> e.getSalary() >= 5000));
System.out.println(map);
}
//
@Test
public void test8(){
String str = emps.stream()
.map(Employee::getName)
.collect(Collectors.joining("," , "----", "----"));
System.out.println(str);
}
@Test
public void test9(){
Optional<Double> sum = emps.stream()
.map(Employee::getSalary)
.collect(Collectors.reducing(Double::sum));
System.out.println(sum.get());
}
5.Optional类
Optional 容器类:用于尽量避免空指针异常
- Optional.of(T t) : 创建一个 Optional 实例
- Optional.empty() : 创建一个空的 Optional 实例
- Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例
- isPresent() : 判断是否包含值
- orElse(T t) : 如果调用对象包含值,返回该值,否则返回t
- orElseGet(Supplier s) :如果调用对象包含值,返回该值,否则返回 s 获取的值
- map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回
- Optional.empty()
flatMap(Function mapper):与 map 类似,要求返回值必须是Optional
public String getGodnessName2(Optional<NewMan> man){
return man.orElse(new NewMan())
.getGodness()
.orElse(new Godness("老师"))
.getName();
@Test
public void test4(){
Optional<Employee> op = Optional.of(new Employee(101, "张三", 18, 9999.99));
Optional<String> op2 = op.map(Employee::getName);
System.out.println(op2.get());
Optional<String> op3 = op.flatMap((e) -> Optional.of(e.getName()));
System.out.println(op3.get());
}
@Test
public void test3(){
Optional<Employee> op = Optional.ofNullable(new Employee());
if(op.isPresent()){
System.out.println(op.get());
}
Employee emp = op.orElse(new Employee("张三"));
System.out.println(emp);
Employee emp2 = op.orElseGet(() -> new Employee());
System.out.println(emp2);
}
6.重复注解与类型注解
7.并行流和串行流
并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。
//返回一个顺序流
Stream<Employee> stream = emps.stream();
//尝试返回一个可能存在的并行流,允许返回顺序流
Stream<Employee> employeeStream = emps.parallelStream();
8.接口中的默认方法和静态方法
8.1默认方法
JAVA8允许接口中包含有具体实现的方法,该方法称为默认方法。使用 default关键字修饰
接口默认方法的”类优先”原则
若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时。
- 选择父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。
- 接口冲突。如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突。
8.2静态方法
java8中,接口中允许添加静态方法。
9.新时间日期API
这一段没有细看,直接贴图了,还望谅解!