Java八股(新特性)

Java8 的新特性

特性名称 描述 示例或说明
Lambda 表达式 简化匿名内部类,支持函数式编程 (a, b) -> a + b 代替匿名类实现接口
函数式接口 仅含一个抽象方法的接口,可用 @FunctionalInterface 注解标记 RunnableComparator, 或自定义接口 @FunctionalInterface interface MyFunc { void run(); }
Stream API 提供链式操作处理集合数据,支持并行处理 list.stream().filter(x -> x > 0).collect(Collectors.toList())
Optional 类 封装可能为 null 的对象,减少空指针异常 Optional.ofNullable(value).orElse("default")
方法引用 简化 Lambda 表达式,直接引用现有方法 System.out::println 等价于 x -> System.out.println(x)
接口的默认方法与静态方法 接口可定义默认实现和静态方法,增强扩展性 interface A { default void print() { System.out.println("默认方法"); } }
并行数组排序 使用多线程加速数组排序 Arrays.parallelSort(array)
重复注解 允许同一位置多次使用相同注解 @Repeatable 注解配合容器注解使用
类型注解 注解可应用于更多位置(如泛型、异常等) List<@NonNull String> list
CompletableFuture 增强异步编程能力,支持链式调用和组合操作 CompletableFuture.supplyAsync(() -> "result").thenAccept(System.out::println)

Lambda 表达式

Lambda 表达式它是一种简洁的语法,用于创建匿名函数,主要用于简化函数式接口(只有一个抽象方法的接口)的使用。其基本语法有以下两种形式:

  • (parameters) -> expression:当 Lambda 体只有一个表达式时使用,表达式的结果会作为返回值。
  • (parameters) -> { statements; }:当 Lambda 体包含多条语句时,需要使用大括号将语句括起来,若有返回值则需要使用 return 语句。

传统的匿名内部类实现方式代码较为冗长,而 Lambda 表达式可以用更简洁的语法实现相同的功能。比如,使用匿名内部类实现 Runnable 接口:

1
2
3
4
5
6
7
8
9
10
11
public class AnonymousClassExample {
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Running using anonymous class");
}
});
t1.start();
}
}

使用 Lambda 表达式实现相同功能:

1
2
3
4
5
6
public class LambdaExample {
public static void main(String[] args) {
Thread t1 = new Thread(() -> System.out.println("Running using lambda expression"));
t1.start();
}
}

可以看到,Lambda 表达式的代码更加简洁明了。

还有,Lambda 表达式能够更清晰地表达代码的意图,尤其是在处理集合操作时,如过滤、映射等。比如,过滤出列表中所有偶数,可以通过流处理来操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class ReadabilityExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
// 使用 Lambda 表达式结合 Stream API 过滤偶数
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println(evenNumbers);
}
}

Java中stream的API

ava 8引入了Stream API,它提供了一种高效且易于使用的数据处理方式,特别适合集合对象的操作,如过滤、映射、排序等。Stream API不仅可以提高代码的可读性和简洁性,还能利用多核处理器的优势进行并行处理。让我们通过两个具体的例子来感受下Java Stream API带来的便利,对比在Stream API引入之前的传统做法。

过滤并收集满足条件的元素

问题场景:从一个列表中筛选出所有长度大于3的字符串,并收集到一个新的列表中。

没有Stream API:

1
2
3
4
5
6
7
8
List<String> originalList = Arrays.asList("apple", "fig", "banana", "kiwi");
List<String> filteredList = new ArrayList<>();

for (String item : originalList) {
if (item.length() > 3) {
filteredList.add(item);
}
}

这段代码需要显式地创建一个新的ArrayList,并通过循环遍历原列表,手动检查每个元素是否满足条件,然后添加到新列表中。

使用Stream API:

1
2
3
4
List<String> originalList = Arrays.asList("apple", "fig", "banana", "kiwi");
List<String> filteredList = originalList.stream()
.filter(s -> s.length() > 3)
.collect(Collectors.toList());

这里,我们直接在原始列表上调用.stream()方法创建了一个流,使用.filter()中间操作筛选出长度大于3的字符串,最后使用.collect(Collectors.toList())终端操作将结果收集到一个新的列表中。代码更加简洁明了,逻辑一目了然。

用于计算

问题场景:计算一个数字列表中所有元素的总和
没有Stream API:

1
2
3
4
5
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = 0;
for (Integer number : numbers) {
sum += number;
}

使用Stream API:

1
2
3
4
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
.mapToInt(Integer::intValue)
.sum();

通过Stream API,我们可以先使用.mapToInt()将Integer流转换为IntStream(这是为了高效处理基本类型),然后直接调用.sum()方法来计算总和,极大地简化了代码。

Stream流的并行API:ParallelStream

并行流(ParallelStream)就是将源数据分为多个子流对象进行多线程操作,然后将处理的结果再汇总为一个流对象,底层是使用通用的 fork/join 池来实现,即将一个任务拆分成多个“小任务”并行计算,再把多个“小任务”的结果合并成总的计算结果。

Stream串行流与并行流的主要区别:

image.png

对CPU密集型的任务来说,并行流使用ForkJoinPool线程池,为每个CPU分配一个任务,这是非常有效率的,但是如果任务不是CPU密集的,而是I/O密集的,并且任务数相对线程数比较大,那么直接用ParallelStream并不是很好的选择。

completableFuture怎么用的

CompletableFuture是由Java 8引入的,在Java8之前我们一般通过Future实现异步。

  • Future用于表示异步计算的结果,只能通过阻塞或者轮询的方式获取结果,而且不支持设置回调方法,Java 8之前若要设置回调一般会使用guava的ListenableFuture,回调的引入又会导致臭名昭著的回调地狱(下面的例子会通过ListenableFuture的使用来具体进行展示)。
  • CompletableFuture对Future进行了扩展,可以通过设置回调的方式处理计算结果,同时也支持组合操作,支持进一步的编排,同时一定程度解决了回调地狱的问题。

下面将举例来说明,我们通过ListenableFuture、CompletableFuture来实现异步的差异。假设有三个操作step1、step2、step3存在依赖关系,其中step3的执行依赖step1和step2的结果。

Future(ListenableFuture)的实现(回调地狱)如下:

…(Todo: 未完成)