JDK 8 新特性的概述
-
jdk版本从jdk9开始每隔半年更新一个版本
-
长期支持的版本(LTS):JDK8(企业使用比例最高的)、JDK11
-
从哪几个角度去关注新版本的特性?
- 语法层面:lambda表达式、switch(字符串)、钻石操作符功能的扩展、接口的静态方法和默认方法和私有方法
- API层面:Stream API、Optional、String、集合类
- 底层优化:JVM底层的优化,GC参数,js的执行引擎
-
Java 8 是oracle公司于2014年3月发布,可以看成是自Java 5 以来最具革命性的版本
-
java8新特性的概述
- 速度更快
- 代码更少(增加了新的语法:Lambda 表达式)
- 强大的 Stream API
- 最大化减少空指针异常:Optional
- Nashorn引擎,允许在JVM上运行JS应用
一、lambda表达式
1.1 规则
-
举例:(o1,o2) -> Integer.compare(o1,o2);
-
格式:
-> 箭头操作符或lambda操作符
->的左边:lambda形参列表,即为原来的抽象方法的形参列表
->的右边:lambda体,即为接口中重写的方法的方法体 -
lambda表达式的本质:作为函数式接口的匿名实现类的对象
-
函数式接口:只声明了一个抽象方法的接口。可以使用@FunctionalInterface修饰
-
使用规则的总结:
->的左边 : lambda形参列表的形参的数据类型可以省略;如果只有一个形参,则一对()可以省略
->的右边: lambda体使用一对{}包裹,如果执行语句只有一行,则此{}可以省略。如果有return,则return也要省略。 -
以前提供函数式接口的实例时,常常提供匿名实现类的对象。现在可以使用lambda表达式替换。
public class LambdaTest1 {
//语法格式一:无参,无返回值
@Test
public void test1(){
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("我爱北京天安门");
}
};
r1.run();
System.out.println("***********************");
Runnable r2 = () -> {
System.out.println("我爱北京天安门");
};
}
//语法格式二:Lambda 需要一个参数,但是没有返回值。
@Test
public void test2(){
Consumer<String> con = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
con.accept("谎言和誓言的区别是什么?");
System.out.println("*******************");
Consumer<String> con1 = (String s) -> {
System.out.println(s);
};
con1.accept("一个是说的人当真的,一个是听的人当真了");
}
//语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
@Test
public void test3(){
Consumer<String> con1 = (String s) -> {
System.out.println(s);
};
con1.accept("一个是说的人当真的,一个是听的人当真了");
System.out.println("*******************");
Consumer<String> con2 = (s) -> {
System.out.println(s);
};
con2.accept("一个是说的人当真的,一个是听的人当真了");
}
//语法格式四:Lambda 若只需要一个参数时,参数的小括号可以省略
@Test
public void test4(){
Consumer<String> con2 = (s) -> {
System.out.println(s);
};
con2.accept("一个是说的人当真的,一个是听的人当真了");
System.out.println("*******************");
Consumer<String> con3 = s -> {
System.out.println(s);
};
con3.accept("一个是说的人当真的,一个是听的人当真了");
}
//语法格式五:Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值
@Test
public void test5(){
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
}
};
System.out.println(com1.compare(12,21));
System.out.println("*****************************");
Comparator<Integer> com2 = (o1, o2) -> {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
}
//语法格式六:当 Lambda 体只有一条语句时,return 与大括号若有,都可以省略
@Test
public void test6(){
Comparator<Integer> com1 = (o1,o2) -> {
return o1.compareTo(o2);
};
System.out.println(com1.compare(12,6));
System.out.println("*****************************");
Comparator<Integer> com2 = (o1,o2) -> o1.compareTo(o2);
}
@Test
public void test7(){
Consumer<String> con1 = s -> {
System.out.println(s);
};
con1.accept("岂曰无衣,与子同裳");
System.out.println("*****************************");
Consumer<String> con2 = s -> System.out.println(s);
con2.accept("岂曰无衣,与子同裳");
}
@Test
public void test8(){
MyInterface m = ()-> System.out.println("hello");
m.method1();
}
}
二、Stream API
2.1 介绍
-
Stream API 之与集合,就如同SQL之与数据库。
-
Stream 和 Collection 集合的区别:Collection 是一种静态的内存数据结构,而Stream 是有关计算的。前者是主要面向内存,存储在内存中,后者主要是面向CPU,通过 CPU 实现计算。
-
注意:
①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
-
Stream操作的三个步骤
-
1.创建 Stream
一个数据源(如:集合、数组),获取一个流 -
2.中间操作
一个中间操作链,对数据源的数据进行处理 -
3.终止操作(终端操作)
一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用
* 1. Stream关注的是对数据的运算,与CPU打交道
* 集合关注的是数据的存储,与内存打交道
*
* 2.
* ①Stream 自己不会存储元素。
* ②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
* ③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行
*
* 3.Stream 执行流程
* ① Stream的实例化
* ② 一系列的中间操作(过滤、映射、...)
* ③ 终止操作
*
* 4.说明:
* 4.1 一个中间操作链,对数据源的数据进行处理
* 4.2 一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用
测试Stream的实例化
public class StreamAPITest {
//创建 Stream通过集合
// @Testam方式一:
public void test1(){
List<Employee> employees = EmployeeData.getEmployees();
// default Stream<E> stream() : 返回一个顺序流
Stream<Employee> stream = employees.stream();
// default Stream<E> parallelStream() : 返回一个并行流
Stream<Employee> stream1 = employees.parallelStream();
}
//创建 Stream方式二:通过数组
@Test
public void test2(){
//调用Arrays类的static <T> Stream<T> stream(T[] array): 返回一个流
Integer[] arr = new Integer[]{1,2,3,4,5};
Stream<Integer> stream = Arrays.stream(arr);
Employee e1 = new Employee();
Employee e2 = new Employee();
Employee[] emps = new Employee[]{e1,e2};
Stream<Employee> stream1 = Arrays.stream(emps);
}
//创建 Stream方式三:通过Stream的of()
@Test
public void test3(){
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
Employee e1 = new Employee();
Employee e2 = new Employee();
Stream<Employee> stream1 = Stream.of(e1, e2);
}
}
Stream的中间操作
public class StreamAPITest1 {
//1-筛选与切片
@Test
public void test1() {
List<Employee> emps = EmployeeData.getEmployees();
// filter(Predicate p)——接收 Lambda , 从流中排除某些元素。
//练习:查询员工表中薪资大于7000的员工信息
Stream<Employee> stream = emps.stream();
stream.filter(e -> e.getSalary() > 7000).forEach(e -> System.out.println(e));
// limit(n)——截断流,使其元素不超过给定数量。
System.out.println();
Stream<Employee> stream1 = emps.stream();
stream1.limit(2).forEach(e -> System.out.println(e));
// skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
System.out.println();
Stream<Employee> stream2 = emps.stream();
stream2.skip(3).forEach(e -> System.out.println(e));
System.out.println();
// distinct()——筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
emps.add(new Employee(1009, "任正非", 65, 12500.32));
emps.add(new Employee(1009, "任正非", 65, 12500.32));
emps.add(new Employee(1009, "任正非", 65, 12500.32));
emps.add(new Employee(1009, "任正非", 65, 12500.32));
emps.add(new Employee(1009, "任正非", 65, 12500.32));
emps.stream().distinct().forEach(e -> System.out.println(e));
}
//2-映射
@Test
public void test2() {
//map(Function f)——接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素。
List<String> list = Arrays.asList("ab", "cc", "jj", "ui");
list.stream().map(str -> str.toUpperCase()).forEach(s -> System.out.println(s));
//练习:获取员工姓名长度大于3的员工的姓名。
List<Employee> emps = EmployeeData.getEmployees();
Stream<Employee> stream = emps.stream();
// stream.filter(e -> e.getName().length() > 3).forEach(s -> System.out.println(s));
Stream<String> stringStream = stream.map(e -> e.getName());
stringStream.filter(s -> s.length() > 3).forEach(s -> System.out.println(s));
}
//3-排序
@Test
public void test3() {
//sorted()——自然排序
Integer[] arr = new Integer[]{5,6,7,23,11,4,65,4};
Stream<Integer> stream = Arrays.stream(arr);
stream.sorted().forEach(i -> System.out.println(i));
//Employee由于没有实现Comparable接口,不能使用自然排序
// Employee e1 = new Employee(1001, "马化腾", 34, 6000.38);
// Employee e2 = new Employee(1002, "马云", 12, 9876.12);
// Employee[] emps = new Employee[]{e1,e2};
// Arrays.stream(emps).sorted().forEach(e -> System.out.println(e));
//sorted(Comparator com)——定制排序
List<Employee> employees = EmployeeData.getEmployees();
employees.stream().sorted((emp1,emp2) -> {
int result = Integer.compare(emp1.getAge(),emp2.getAge());
if(result != 0){
return result;
}
return Double.compare(emp1.getSalary(),emp2.getSalary());
}).forEach(i -> System.out.println(i));
}
}
Stream的终止操作
public class StreamAPITest2 {
//1-匹配与查找
@Test
public void test1(){
List<Employee> employees = EmployeeData.getEmployees();
// allMatch(Predicate p)——检查是否匹配所有元素。
// 练习:是否所有的员工的年龄都大于18
boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18);
System.out.println(allMatch);
// anyMatch(Predicate p)——检查是否至少匹配一个元素。
// 练习:是否存在员工的工资大于 10000
boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000);
System.out.println(anyMatch);
// findFirst——返回第一个元素
Optional<Employee> optional = employees.stream().findFirst();
System.out.println(optional.get());
}
@Test
public void test2(){
List<Employee> employees = EmployeeData.getEmployees();
// count——返回流中元素的总个数
System.out.println(employees.stream().filter(e -> e.getAge() > 18).count());
// max(Comparator c)——返回流中最大值
// 练习:返回最高的工资:
Stream<Double> doubleStream = employees.stream().map(e -> e.getSalary());
Optional<Double> optional = doubleStream.max((s1, s2) -> Double.compare(s1, s2));
System.out.println(optional.get());
// min(Comparator c)——返回流中最小值
// 练习:返回最低工资的员工
Employee employee = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())).get();
System.out.println(employee);
// forEach(Consumer c)——内部迭代
//略
employees.stream().forEach(System.out::println);
//集合中新增的方法:forEach
employees.forEach( e-> System.out.println(e));
}
//2-归约
@Test
public void test3(){
// reduce(T identity, BinaryOperator)——可以将流中元素反复结合起来,得到一个值。返回 T
// 练习1:计算1-10的自然数的和
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = list.stream().reduce(0, (x1, x2) -> x1 + x2);
System.out.println(sum);
// reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值。返回 Optional<T>
// 练习2:计算公司所有员工工资的总和
Stream<Employee> stream = EmployeeData.getEmployees().stream();
Stream<Double> stream1 = stream.map(e -> e.getSalary());
Optional<Double> optional = stream1.reduce((s1, s2) -> s1 + s2);
System.out.println(optional.get());
}
//3-收集
@Test
public void test4(){
// collect(Collector c)——将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
// 练习1:查找工资大于6000的员工,结果返回为一个List或Set
Stream<Employee> stream = EmployeeData.getEmployees().stream();
List<Employee> list = stream.filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
list.forEach(System.out::println);
}
}