学妹问我 JDK1.8 的新特性,我这样帮她总结

0 引子

学妹:师兄师兄!我去面试被问到 JDK1.8 的新特性这个问题,不知道怎么回答啊!哭了哭了,师兄能帮我总结一下 JDK1.8 的新特性吗?

我:小意思!学妹你先坐下,且让师兄为你慢慢讲解(嘻嘻)

1 前言

了解 JDK1.8 的新特性,无论是面试需要还是工作要求,对我们都是非常重要的,本文会介绍几种 JDK1.8 的新特性,希望能够对大家有所帮助。

2 JDK1.8 特性:Lambda 表达式

Lambda 表达式是一个匿名函数,Lambda 表达式没有声明的方法,也没有访问修饰符、返回值声明和名字,用于帮助我们写出更简洁、更灵活的代码。

Lambda 表达式建立在函数式接口之上,那什么是函数式接口呢?只包含一个抽象方法的接口就被称为函数式接口,我们可以通过 Lambda 表达式来创建函数式接口的对象。

下面我们举个栗子,相信大家经常使用排序功能吧,这里我们使用 Comparator 接口对 Lambda 表达式的使用做一个测试。

我们先使用匿名内部类来实现 Comparator 接口。

public class Test {

public static void main(String[] args){

String[] array = {"apple","string","ss"};

//匿名内部类的使用

Arrays.sort(array,new Comparator() {

@Override

public int compare(String o1, String o2) {

return o1.length() - o2.length();

}

}); 

}

}

然后再使用 Lambda 表达式实现 Comparator 接口。

public class Test {

public static void main(String[] args){

String[] array = {"apple","string","ss"};

//使用 Lambda 表达式

Arrays.sort(array, (String s1, String s2) -> (s1.length() - s2.length()));

}

}


可以发现使用 Lambda 表达式更为简洁。

我们之前提到过 Lambda 表达式建立在函数式接口之上,为什么呢?其实是为了保证唯一,我们上面的 Comparator 接口只有一个抽象方法,使用 Lambda 表达式可以代表那个方法,如果接口有多个抽象方法,谁知道 Lambda 表达式代表的是哪一个抽象方法呢?

3 JDK1.8 特性:函数式接口

我们在介绍 Lambda 表达式时其实已经讲得很清楚了,函数式接口是只有一个抽象函数的接口,在 JDK1.8 中提供了@FunctionalInterface 注解来检查函数式接口的合法性。

下面我们介绍几种基础的函数式接口:

Consumer

消费者接口,有参无返回值,用于消费数据。

Consumer 接口中提供了 accept 抽象方法,accept 方法会接受一个变量,即使用该函数式接口时会提供数据,只需接受使用即可。

public class Test {

//使用消费型接口

public static void change(Consumer con,String str) {

con.accept(str);

}

public static void main(String[] args){

change((str) -> System.out.println(str),"Hello World!");

}

}


Function

函数式接口,有参有返回值,提供转换功能。

Function 接口中提供了 apply 抽象方法,apply 接受 T 类型数据并返回 R 类型数据。

public class Test {

//使用函数式接口

public static Integer change(Function fun,Integer i) {

return fun.apply(i);

}

public static void main(String[] args){

Integer num = change((x) -> 2 * x,100);

System.out.println(num);

}

}


Predicate

断言型接口,有参有返回值,其中会返回一个布尔类型的变量,提供断言、判断功能。

Predicate 接口中提供了 test 抽象方法,对传入的数据进行判断,返回 boolean 类型。

public class Test {

//使用断言型接口

public static boolean change(Predicate pre,String str) {

return pre.test(str);

}

public static void main(String[] args){

boolean res = change((str) -> str.equals("Hello"),"Hello");

System.out.println(res);

}

}


Supplier

供给型接口,无参有返回值,用于生产数据。

Supplier 接口中提供了 get 抽象方法,用于返回数据。

public class Test {

//使用供给型接口

public static String change(Supplier sup) {

return sup.get();

}

public static void main(String[] args){

String str = change(() -> "Hello World!");

System.out.println(str);

}

}


函数式接口事实上是为了更加方便地使用 Lambda 表达式,有了 JDK1.8 提供的函数式接口,我们不需要手动创建函数式接口,直接使用即可。

4 JDK1.8 特性:方法引用

我们经常使用 Lambda 表达式来创建匿名方法,但有时我们只是调用一个已经存在的方法而已。在 JDK1.8 中,可以通过方法引用来简写 Lambda 表达式中已经存在的方法。

方法引用是一种更简洁易懂的 Lambda 表达式,其操作符为双冒号 :: 。方法引用是用来直接访问类或者实例的已经存在的方法,它提供了一种引用而不执行方法的方式。

如果 Lambda 表达式仅仅调用一个已存在的方法而不做任何其它事,通过一个方法名字来引用这个已存在的方法也许会更加清晰,Java 8 的方法引用允许我们这样操作。

下面我们举个栗子,对一个 Integer 的封装类进行排序。

Integer 的封装类代码如下:

   class NewInteger{

private Integer num;

public NewInteger(Integer num) {

this.num = num;

}

public int getNum() {

return num;

}

public static int compare(NewInteger a,NewInteger b) {

return a.getNum() - b.getNum();

}

}


对其进行排序,我们可以使用匿名内部类写法:

public class Test {

public static void main(String[] args){

NewInteger[] array = {new NewInteger(1),new NewInteger(12),new NewInteger(-1),new NewInteger(34)};

//匿名内部类写法

Arrays.sort(array, new Comparator() {

@Override

public int compare(NewInteger o1, NewInteger o2) {

return o1.getNum() - o2.getNum();

}

        });

}

}


我们可以发现,Comparator 是一个函数式接口,故我们可以使用 Lambda 表达式,写法如下:

public class Test {

public static void main(String[] args){

NewInteger[] array = {new NewInteger(1),new NewInteger(12),new NewInteger(-1),new NewInteger(34)};

//Lambda表达式写法

Arrays.sort(array, (NewInteger a,NewInteger b) -> {

return a.getNum() - b.getNum();

});

}

}


其实,我们之前在 Integer 的封装类中已经定义了一个比较方法,因此我们可以直接使用该比较方法:

public class Test {

public static void main(String[] args){

NewInteger[] array = {new NewInteger(1),new NewInteger(12),new NewInteger(-1),new NewInteger(34)};

//Lambda表达式写法

Arrays.sort(array, (a ,b) -> NewInteger.compare(a, b));

}

}


由于 Lambda 表达式调用了一个已存在的方法,因此,我们可以使用方法引用来替代这个 Lambda 表达式。

public class Test {

public static void main(String[] args){

NewInteger[] array = {new NewInteger(1),new NewInteger(12),new NewInteger(-1),new NewInteger(34)};

//方法引用写法

Arrays.sort(array, NewInteger::compare);

}

}


方法引用 NewInteger::compare 与 Lambda 表达式 (a ,b) -> NewInteger.compare(a, b) 是等价的。

方法引用的标准形式是 类名::方法名,一共有四种形式的方法引用,分别为引用静态方法,引用某个对象的实例方法,引用某个类型的任意对象的实例方法,引用构造方法。我们之前所举例子就是一个静态方法引用。

5 JDK1.8 特性:Stream API

由于文章篇幅关系,本文不准备详细介绍 Stream 的各类 API,只会介绍一下 Stream 的概念,并举一个使用 Stream 的小例子,关于 Stream 的各类 API,会在我的下一篇文章为大家一一介绍。

Stream 是一个处理集合的关键抽象概念,可以对集合进行各种操作,Stream API 为我们操作集合提供了强大的功能,同时操作简单,容易上手。

Stream 一般有如下三个操作步骤:

创建 Stream:从一个数据源(集合、数组)中获取流中间操作:一个操作的中间链,对数据源的数据进行操作终止操作:一个终止操作,执行中间操作链,并产生结果

注意,对流的操作完成后需要对其进行关闭。

如何理解 Stream?我们可以这么想,集合的要点在于数据,流的要点在于计算。Stream 既不会存储元素,也不会改变源对象,且会返回一个持有结果的新的 Stream,另外,Stream 的操作是延迟执行的,只有在需要结果时,才会执行。

我们现在实现一个小功能,计算集合中大于 10 的元素数量,在 JDK1.8 之前,我们一般这样实现:

public class Test {

public static void main(String[] args){

List list = new ArrayList();

list.add(34);

list.add(7);

list.add(2);

list.add(66);

list.add(-3);

list.add(71);

int count = 0;

for (Integer integer : list) {

if(integer > 10) count++;

}

System.out.println("集合中大于10的元素数量:" + count );

}

}

如何使用 Stream API 的话,我们可以这样写代码:

public class Test {

public static void main(String[] args){

List list = new ArrayList();

list.add(34);

list.add(7);

list.add(2);

list.add(66);

list.add(-3);

list.add(71);

long count = list.stream().filter(i -> i > 10).count();

System.out.println("集合中大于10的元素数量:" + count );

}

}


使用 Stream API 一行代码就解决了,是不是很简单呢!

6 JDK1.8 特性:default 关键字

在 JDK1.8 之前,接口不能提供方法的实现,但在 JDK1.8 之后,我们可以为方法提供默认实现方法和静态方法,分别用关键字 default 和 static 修饰即可。

如果一个类既继承父类又实现接口时,两者方法名相同,类优先;如果实现两个同名方法的接口,则实现类必须手动声明默认实现哪个接口中的方法。

interface Car {

void begin();

//默认实现方法

default void play() {

System.out.println("正在开车");

}

//静态方法

static void end() {

System.out.println("结束开车");

}

}

class Porsche implements Car {

@Override

public void begin() {

System.out.println("保时捷启动");

}

}

public class Test {

public static void main(String[] args){

Car porsche = new Porsche();

porsche.begin();

porsche.play();

Car.end();

}

}

7 总结

JDK1.8 的几个主要新特性我都简单介绍了一下,还有一些别的特性,例如 Optional,Date API 等,由于文章篇幅问题,只能一笔带过。面试能够讲到上面几点,通过应该是没问题的,但在工作中运用却是远远不够。冰冻三尺,非一日之寒,共勉!

————————————————

版权声明:本文为CSDN博主「Geffin」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/Geffin/article/details/105069890

你可能感兴趣的:(学妹问我 JDK1.8 的新特性,我这样帮她总结)