JDK1.8的新特性有哪些
一. Lambda表达式
1. 需求分析
创建一个新的线程,指定线程要执行的任务
public class Demo01Lambda {
public static void main(String[] args) {
//开启一个新的线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("新线程中执行的代码"+Thread.currentThread().getName());
}
}).start();
System.out.println("主线程中的代码"+Thread.currentThread().getName());
}
}
2. 代码分析:
- Thread类需要一个Runnable接口作为其参数,其中的抽象方法run方法是用来指定线程任务内容的核心。
- 为了指定run方法体,不得不需要Runnable的实现类。
- 为了省去定义一个Runnable的实现类,不得不使用匿名内部类。
- 必须覆盖重写抽象的run方法,所有的方法名称,方法参数,方法返回值不得不都重写一遍,而且不能出错。
- 而实际上,我们只在乎方法体中的代码。
3. 使用Lambda表达式
由此我们可以通过使用Lambda表达式,来减轻书写代码负担,简化了匿名内部类的使用。
public class Demo01Lambda {
public static void main(String[] args) {
//开启一个新的线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("新线程中执行的代码"+Thread.currentThread().getName());
}
}).start();
System.out.println("主线程中的代码"+Thread.currentThread().getName());
System.out.println("----------------");
new Thread(()->{
System.out.println("新线程Lambda表达式"+Thread.currentThread().getName());
}).start();
}
}
4. Lambda表达式语法规则
(参数类型 参数名称) ->{
代码体
}
5. @FunctionalInterface注解
被该注解修饰的接口只能声明一个抽象方法。
6. Lambda表达式的使用前提
- 方法中参数或局部变量类型必须为接口才能使用Lambda
- 接口中有且仅有一个抽象方法(@FunctionalInterface)
二. 方法引用
1. 为什么要用方法的引用
Lambda表达式会出现代码冗余的情况。
2. 方法引用的格式
符号表示::: 符号说明:双冒号为方法引用运算符,而它所在的表达式被称为方法引用。 应用场景:如果Lambda表达式所要实现的方案,已经有其他方法存在相同的方案,那么则可以使用方法引用。 常见引用方式:
- instanceName::methodName 对象::方法名
- ClassName::staticMethodName 类名::静态方法
- ClassName::methodName 类名::普通方法
- ClassName::new 类名::new调用的构造器
- TypeName[]::new String[]::new 调用数组的构造器
3. 方法引用案例
public class Test {
public static void main(String[] args) {
Date now =new Date();
Supplier<Long> supplier = ()->{
return now.getTime();
};
System.out.println(supplier.get());
//如果通过方法的引用来实现
Supplier<Long> supplier1 = now::getTime;
System.out.println(supplier1.get());
}
}
三. Java中stream的API
Java 8引入了Stream API,它提供了一种高效且易于使用的数据处理方式,特别适合集合对象的操作,如过滤、映射、排序等。Stream API不仅可以提高代码的可读性和简洁性,还能利用多核处理器的优势进行并行处理。让我们通过两个具体的例子来感受下Java Stream API带来的便利,对比在Stream API引入之前的传统做法。
1. 过滤并收集满足条件的元素
问题场景:从一个列表中筛选出所有长度大于3的字符串,并收集到一个新的列表中。
没有Stream API的做法:
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的做法:
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())终端操作将结果收集到一个新的列表中。代码更加简洁明了,逻辑一目了然
2. 计算列表中所有数字的总和
问题场景:计算一个数字列表中所有元素的总和。
没有Stream API的做法:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = 0;
for (Integer number : numbers) {
sum += number;
}
这个传统的for-each循环遍历列表中的每一个元素,累加它们的值来计算总和。
使用Stream API的做法:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
.mapToInt(Integer::intValue)
.sum();
通过Stream API,我们可以先使用.mapToInt()将Integer流转换为IntStream(这是为了高效处理基本类型),然后直接调用.sum()方法来计算总和,极大地简化了代码。
四. 函数式接口
我们知道使用Lambda表达式的前提是需要有函数式接口,而Lambda表达式使用时不关心接口名,抽象方法名。只关心抽象方法的参数列表和返回值类型。因此为了让我们使用Lambda表达式更加的方便,在JDK中提供了大量常用的函数式接口。
1. 函数式接口的介绍
Supplier 无参有返回值的接口
Function 有参有返回值的接口
2. 举例
以supplier为例
public class Test {
public static void main(String[] args) {
fun(()->{
int arr[]={1,2,3,55,5};
//计算数组中最大值
Arrays.sort(arr);
return arr[arr.length-1];
});
}
public static void fun(Supplier<Integer> supplier) {
Integer max = supplier.get();
System.out.println("max = "+max);
}
}
