文章目录
- 1.Java8新特性概述
- 2.函数式接口
-
- 2.1.函数式接口与lambda表达式的简单使用示例
- 2.2.四大核心函数式接口
-
- 2.2.1.Consumer接口
- [2.2.2. Supplier接口](#2.2.2. Supplier接口)
- [2.2.3. Function接口](#2.2.3. Function接口)
- [2.2.4. Predicate接口](#2.2.4. Predicate接口)
- 2.3.其他函数接口
- 3.lambda表达式
- 4.方法引用
- [5.Stream API概述](#5.Stream API概述)
- 6.Optional类介绍
- 7.新时间日期API
- [8. JDK8 HashMap](#8. JDK8 HashMap)
1.Java8新特性概述
java8的新特性有函数式接口、接口的默认方法与静态方法、Lambda表达式、方法引用与构造器引用、Stream API、新时间日期API、减少空指针异常Optional、支持重复注解、Fork/Join 框架等。
java8的优点如下:速度更快、代码更少(增加了新的语法Lambda表达式)、强大的Stream API、便于并行、最大化减少空指针异常Optional。
2.函数式接口
Lambda表达式需要函数式接口的支持,所以,我们有必要来说说什么是函数式接口。
- 只有一个抽象方法的接口
- 函数式接口上可以使用 @FunctionalInterface 注解也可以不使用
- 函数式接口里可以定义默认方法:默认方法有方法体,不是抽象方法。
- 函数式接口里可以定义静态方法:静态方法也不是抽象方法,是一个有具体方法实现的方法
- 函数式接口里可以定义Object里的public方法(改成抽象方法):虽然它们是抽象方法,却不需要覆盖重写,因为所有接口的实现类都是Object类的子类,而在Object类中有这些方法的具体的实现。
2.1.函数式接口与lambda表达式的简单使用示例
例如下面使用函数式接口和Lambda表达式实现对字符串的处理功能。
定义函数式接口MyFunc,如下所示
java
@FunctionalInterface
public interface MyFunc <T> {
public T getValue(T t);
}
接下来,定义一个操作字符串的方法,其中参数为MyFunc接口实例和需要转换的字符串。
java
public String handlerString(MyFunc<String> myFunc, String str){
return myFunc.getValue(str);
}
接下来,我们对自定义的函数式接口进行测试,此时我们传递的函数式接口的参数为Lambda表达式,并且将字符串转化为大写。
java
@Test
public void test6(){
String str = handlerString((s) -> s.toUpperCase(), "hello world");
System.out.println(str);
}
也可以截取字符串的某一部分,如下所示:
java
@Test
public void test7(){
String str = handlerString((s) -> s.substring(0,4), "hello world");
System.out.println(str);
}
通过handlerString(MyFunc myFunc, String str)方法结合Lambda表达式对字符串进行任意操作。
注意:作为参数传递 Lambda 表达式:为了将 Lambda 表达式作为参数传递,接收Lambda 表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口的类型 。
2.2.四大核心函数式接口
函数式接口 | 参数类型 | 返回类型 | 使用场景 |
---|---|---|---|
Consumer消费型接口 | T | void | 对类型为T的对象应用操作,接口定义的方法:void accept(T t) |
Supplier供给型接口 | 无 | T | 返回类型为T的对象,接口定义的方法:T get() |
Function<T,R>函数式接口 | T | R | 对类型为T的对象应用操作,并R类型的返回结果。接口定义的方法:R apply(T t) |
Predicate断言型接口 | T | boolean | 确定类型为T的对象是否满足约束条件,并返回boolean类型的数据。接口定义的方法:boolean test(T t) |
2.2.1.Consumer接口
使用示例:
java
public void handlerConsumer(Integer number, Consumer<Integer> consumer){
consumer.accept(number);
}
@Test
public void test1(){
this.handlerConsumer(10000, (i) -> System.out.println(i));
}
2.2.2. Supplier接口
使用示例:
java
public List<Integer> getNumberList(int num, Supplier<Integer> supplier){
List<Integer> list = new ArrayList<>();
for(int i = 0; i < num; i++){
list.add(supplier.get())
}
return list;
}
@Test
public void test2(){
List<Integer> numberList = this.getNumberList(10, () -> new Random().nextInt(100));
numberList.stream().forEach(System.out::println);
}
2.2.3. Function接口
使用示例:
java
public String handlerString(String str, Function<String, String> func){
return func.apply(str);
}
@Test
public void test3(){
String str = this.handlerString("binghe", (s) -> s.toUpperCase());
System.out.println(str);
}
2.2.4. Predicate接口
使用示例:
java
public List<String> filterString(List<String> list, Predicate<String> predicate)
{
List<String> strList = new ArrayList<>();
for(String str : list){
if(predicate.test(str)){
strList.add(str);
}
}
return strList;
}
@Test
public void test4(){
List<String> list = Arrays.asList("Hello", "Lambda", "binghe", "lyz", "World");
List<String> strList = this.filterString(list, (s) -> s.length() >= 5);
strList.stream().forEach(System.out::println);
}
2.3.其他函数接口
函数式接口 | 参数类型 | 返回类型 | 使用场景 |
---|---|---|---|
BiFunction(T, U, R) | T, U | R | 对类型为T,U的参数应用操作,返回R类型的结果。接口定义的方法:R apply(T t, U u) |
UnaryOperator(Function子接口) | T | T | 对类型为T的对象进行一 元运算, 并返回T类型的 结果。 包含方法为 T apply(T t) |
BinaryOperator (BiFunction 子接口) | T, T | T | 对类型为T的对象进行二 元运算, 并返回T类型的 结果。 包含方法为 T apply(T t1,T t2) |
BiConsumer<T, U> | T, U | void | 对类型为T, U 参数应用 操作。 包含方法为 void accept(T t, U u) |
ToIntFunction | T | int | 计算int值的函数 |
ToLongFunction | T | long | 计算long值的函数 |
ToDoubleFunction | T | double | 计算double值的函数 |
IntFunction | int | R | 参数为int 类型的函数 |
LongFunction | long | R | 参数为 long类型的函数 |
DoubleFunction | double | R | 参数为double类型的函数 |
3.lambda表达式
Lambda表达式是一个匿名函数,本质上是对接口的实现,Lambda是一段可以传递的代码(能够做到将代码像数据一样进行传递)。
匿名内部类,例如,我们使用匿名内部类比较两个Integer类型数据的大小。
java
Comparator<Integer> com = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
在上述代码中,我们使用匿名内部类实现了比较两个Integer类型数据的大小。
接下来,我们就可以将上述匿名内部类的实例作为参数,传递到其他方法中了,如下所示。
java
TreeSet<Integer> treeSet = new TreeSet<>(com)
使用Lambda表达式完成两个Integer类型数据的比较。
java
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
3.1.Lambda表达式的语法
Lambda表达式在Java语言中引入了 "->" 操作符, "->" 操作符被称为Lambda表达式的操作符或者箭头操作符,它将Lambda表达式分为两部分:
- 左侧部分指定了Lambda表达式需要的所有参数。
Lambda表达式本质上是对接口的实现,Lambda表达式的参数列表本质上对应着接口中方法的参数列表。 - 右侧部分指定了Lambda体,即Lambda表达式要执行的功能。
Lambda体本质上就是接口方法具体实现的功能。
常用的Lambda表达式的语法总结如下。
1.语法格式一:无参,无返回值,Lambda体只有一条语句
java
Runnable r = () -> System.out.println("Hello Lambda");
2.语法格式二:Lambda表达式需要一个参数,并且无返回值
java
Consumer<String> func = (s) -> System.out.println(s);
3.语法格式三:Lambda只需要一个参数时,参数的小括号可以省略
java
Consumer<String> func = s -> System.out.println(s);
4.语法格式四:Lambda需要两个参数,并且有返回值
java
BinaryOperator<Integer> bo = (a, b) -> {
System.out.println("函数式接口");
return a + b;
};
5.语法格式五:当Lambda体只有一条语句时,return和大括号可以省略
java
BinaryOperator<Integer> bo = (a, b) -> a + b;
6.语法格式六:Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器能够通过上下文推断出数据类型,这就是"类型推断"
java
BinaryOperator<Integer> bo = (Integer a, Integer b) -> {
return a + b;
};
等同于
java
BinaryOperator<Integer> bo = (a, b) -> {
return a + b;
};
上述 Lambda 表达式中的参数类型都是由编译器推断得出的。 Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。 Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的"类型推断"。
4.方法引用
5.Stream API概述
6.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
7.新时间日期API
8. JDK8 HashMap
- JDK7 HashMap结构为数组+链表(发生元素碰撞时,会将新元素添加到链表开头)
- JDK8 HashMap结构为数组+链表+红黑树(发生元素碰撞时,会将新元素添加到链表末尾,当HashMap总容量大于等于64,并且某个链表的大小大于等于8,会将链表转化为红黑树(注意:红黑树是二叉树的一种))
JDK8 HashMap重排序:如果删除了HashMap中红黑树的某个元素导致元素重排序时,不需要计算待重排序的元素的HashCode码,只需要将当前元素放到(HashMap总长度+当前元素在HashMap中的位置)的位置即可。