jdk1.8新特性学习与总结

前言


现在常用的jdk版本就是1.8的版本,1.8版本是一次重大的版本升级,本篇简单总结一下常用的一些新特性。

一、Lambda表达式

Lambda表达式是Java8的最重要的一点更新,其中涉及到了函数接口这个概念,先来简单看看用法。

在之前我们要为一个List做升序排序的时候,通常的写法是写成匿名内部类的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
List<Integer> list = Arrays.asList(4, 3, 5, 5, 1, 9, 6);
Collections.sort(list, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
});
/** 不用静态方法Collections.sort()也可
list.sort(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
});
**/

用Lambda表达式简化过可以写成

1
2
3
List<Integer> list = Arrays.asList(4, 3, 5, 5, 1, 9, 6);
Collections.sort(list, (a, b) -> a.compareTo(b.intValue()));
//list.sort((a, b) -> a.compareTo(b.intValue()));

在上面的Lambda表达式中,Java编译器可以自动推导出参数类型,所以你可以不用再写一次类型。

之前提到了函数接口的概念,它允许我们将函数当成参数传递给某个方法,或者把代码本身当作数据处理。比如下面这段代码实现遍历的效果

1
2
3
4
5
6
List<Integer> list = Arrays.asList(4, 3, 5, 5, 1, 9, 6);
list.forEach(e -> System.out.println(e));
// 如果要实现更复杂的语句,只需要加上{}
list.forEach(e -> {System.out.println(e);
System.out.println();}
);

稍微复杂一些,如果需要遍历的是对象,可以写成下面这段。

1
2
3
4
5
6
7
8
9
10
Student s1 = new Student("peter", "Beijing", 1);
Student s2 = new Student("Mark", "Hangzhou", 2);
Student s3 = new Student("Marry", "Shanghai", 3);
List<Student> list = new ArrayList<>();
list.add(s1);
list.add(s2);
list.add(s3);
list.forEach(s -> {System.out.println(s.getName());
System.out.println(s.getAddress());
System.out.println(s.getId());});

好了,回到之前的函数式接口的概念,Lambda之所以能在Java中使用,归功于Java中提供了一个@FunctionalInterface的注解。函数式接口就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口,那么就可以使用Lambda表达式来表示该接口的一个实现,其实就是匿名内部类的简化写法。比如上面的Comparator

1
2
3
4
5
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
//...
}

(参数) -> 方法体的这种写法其实就是简化了匿名内部类的写法。

二、Stream接口

Stream多用于集合的计算,Stream 操作分为中间操作或者最终操作两种,最终操作返回一特定类型的计算结果,而中间操作返回Stream本身,这样你就可以将多个操作依次串起来。Stream 的创建需要指定一个数据源,比如 java.util.Collection的子类,List或者Set, Map不支持。Stream的操作可以串行执行或者并行执行。

集合接口有两个办法来生成流,stream()是生成串行流,parallelStream()是生成并行流。下面举几个例子

1
2
3
4
5
6
7
8
9
10
11
12
List<Integer> list = Arrays.asList(4, 3, 5, 5, 1, 9, 6);

// map用于映射每个元素到对应的结果,比如以下代码段是输出对应元素的平方
list.stream().map(i -> i*i).forEach(i -> System.out.println(i));
// collect 配合Collector这个工具类,根据流生成不同的集合,比如下面的代码段是将生成的结果保存到一个集合list2中
List<Integer> list2 = list.stream().map(i -> i*i).collect(Collectors.toList());
// filter 方法用于通过设置的条件过滤出元素。比如下面的代码段是将小于5的数字输出
list.stream().filter(i -> i<5).forEach(i -> System.out.println(i));
// limit 方法用于获取指定数量的流。比如下面的代码段是打印出数量为5的元素。
list.stream().limit(5).forEach(i -> System.out.println(i));
// sorted 方法用于对流进行排序。比如下面的代码段是对list进行降序排序并输出。
list.stream().sorted((a, b) -> b.compareTo(a.intValue())).forEach(i -> System.out.println(i));

比较复杂一些的例子有

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Student s1 = new Student("peter", "Beijing", 1);
Student s2 = new Student("Mark", "Hangzhou", 1);
Student s3 = new Student("Marry", "Shanghai", 3);
Student s4 = new Student("Tom", "Quzhou", 3);
Student s5 = new Student("Cofe", "Shandong", 5);
List<Student> list = new ArrayList<>();
list.add(s1);
list.add(s2);
list.add(s3);
list.add(s4);
list.add(s5);

// 对id进行分组,同一个id的放在一个List中
Map<Integer, List<Student>> map = list.stream().collect(Collectors.groupingBy(Student::getId));
// 根据id进行排序。降序排序,最后打印出姓名。
list.stream().sorted(Comparator.comparing(Student::getId).reversed()).forEach(s -> System.out.println(s.getName()));
// 提取出姓名,根据长度进行排序并输出结果。
list.stream().map(s -> s.getName()).sorted(Comparator.comparing(String::length)).forEach(s -> System.out.println(s));

三、Optional接口

对于深度嵌套的语句,可能需要多次判空,才能保证代码的健壮性,但是用if来实现,会有一堆的if语句,java8通过optinal比较优雅的解决了这个问题。Optional是个用来防止NullPointerException异常的辅助类型,Optional 被定义为一个简单的容器,其值可能是null或者不是null。但是真正要发挥Optional能力的用法需要Optional和Lambda一起使用。

下面举例简单介绍。

1
2
3
4
5
6
7
8
9
10
11
// 在之前写这种选择分支的语句我们通常会这么写
public static String getName(Student student) {
if (student == null)
return "null";
else
return student.getName();
}
// 在jdk8中用Optional和Lambda我们可以这么写
public static String getName(Student student) {
return Optional.ofNullable(student).map(s -> s.getName()).orElse("null");
}

其中map和flatMap的区别是:

map中获取的返回值自动被Optional包装,即返回值 -> Optional<返回值>

flatMap中返回值保持不变,但必须是Optional类型,即Optional<返回值> -> Optional<返回值>