对话框 未能返回新代码元素 可能 没有ncb_JDK1.8的新特性 | 技术

5eb5057d42974ded67138bc2ef694e97.png

黑马程序员

微信号:heiniu526

传智播客旗下互联网资讯,学习资源免费分享平台

JDK1.8目前在企业中已经广泛被应用,今天我们将学习以下方面的新特性:

· Lambda表达式

· 函数式接口

· 方法引用

· 接口的默认方法和静态方法

· Optional

· Streams

· 并行数组

Lambda表达式

Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。可以使代码变的更加简洁紧凑。

⒈基本语法

  (参数列表) -> {代码块}  

需要注意:

· 参数类型可省略,编译器可以自己推断

· 如果只有一个参数,圆括号可以省略

· 代码块如果只是一行代码,大括号也可以省略

· 如果代码块是一行,且是有结果的表达式, return 可以省略

注意:事实上,把Lambda表达式可以看做是匿名内部类的一种简写方式。当然,前提是这个匿名内部类对应的必须

是接口,而且接口中必须只有一个函数!Lambda表达式就是直接编写函数的:参数列表、代码体、返回值等信息,

用函数来代替完整的匿名内部类 !

⒉用法示例

示例1:多个参数

准备一个集合:

df58e0ef7fe433ae2a499e2d43f74e9e.png

假设我们要对集合排序,我们先看JDK7的写法,需要通过匿名内部类来构造一个 Comparator :

3ef244fed1c84e4469acc4775a63b79c.png

如果是jdk8,我们可以使用新增的集合API:sort(Comparator c) 方法,接收一个比较器,我们用Lambda来代替Comparator 的匿名内部类:

f56e127abdd5d793f66c726c80330115.png

对比一下 Comparator 中的 compare() 方法,你会发现:这里编写的Lambda表达式,恰恰就是 compare() 方法的简写形式,JDK8会把它编译为匿名内部类。是不是简单多了!

别着急,我们发现这里的代码块只有一行代码,符合前面的省略规则,我们可以简写为:

60e2435ff2cb53a06ac40ca96362aeef.png

示例2:单个参数

还以刚才的集合为例,现在我们想要遍历集合中的元素,并且打印。

先用jdk1.7的方式:

a66cd8e7d782020d5a0ca00eeb961dc3.png

jdk1.8给集合添加了一个方法:foreach() ,接收一个对元素进行操作的函数:

248624adf6b8dcd2a9086dcb4177f0df.png

实例3:把Lambda赋值给变量

Lambda表达式的实质其实还是匿名内部类,所以我们其实可以把Lambda表达式赋值给某个变量。

2ee88174882a57e7b8992179b6fdd1cd.png

不过上面的用法很少见,一般都是直接把Lambda作为参数。

示例4:隐式final

Lambda表达式的实质其实还是匿名内部类,而匿名内部类在访问外部局部变量时,要求变量必须声明为 final !不过我们在使用Lambda表达式时无需声明 final ,这并不是说违反了匿名内部类的规则,因为Lambda底层会隐式的把变量设置为 final ,在后续的操作中,一定不能修改该变量:

正确示范:

fd2e141beec5d71ef9034d0391940b98.png

错误案例:

d2f187496021006c9740361de07f6cdf.png

函数式接口

经过前面的学习,相信大家对于Lambda表达式已经有了初步的了解。总结一下:

· Lambda表达式是接口的匿名内部类的简写形式

· 接口必须满足:内部只有一个函数

其实这样的接口,我们称为函数式接口,我们学过的 Runnable 、Comparator 都是函数式接口的典型代表。但是在实践中,函数接口是非常脆弱的,只要有人在接口里添加多一个方法,那么这个接口就不是函数接口了,就会导致编译失败。Java 8提供了一个特殊的注解@FunctionalInterface 来克服上面提到的脆弱性并且显示地表明函数接口。而且jdk8版本中,对很多已经存在的接口都添加了@FunctionalInterface 注解,例如 Runnable 接口:

另外,Jdk8默认提供了一些函数式接口供我们使用:

⒈Function类型接口

3bb74c094bbff21a19973b4a90fb966d.png

Function代表的是有参数,有返回值的函数。还有很多类似的Function接口:

e6c21029ded55454a74ea17cbdde913b.png

看出规律了吗?这些都是一类函数接口,在Function基础上衍生出的,要么明确了参数不确定返回结果,要么明确结果不知道参数类型,要么两者都知道。

⒉Consumer系列

cb035f2d755ec33f0bbe9411bdd04f24.png

Consumer系列与Function系列一样,有各种衍生接口,这里不一一列出了。不过都具备类似的特征:那就是不返回任何结果。

⒊ Predicate系列

5fbfdfddb53ecfd1c462a3498330c12f.png

Supplier系列,英文翻译就是“供应者”,顾名思义:只产出,不收取。所以不接受任何参数,返回T类型结果。

方法引用

方法引用使得开发者可以将已经存在的方法作为变量来传递使用。方法引用可以和Lambda表达式配合使用。

⒈语法

总共有四类方法引用:

5bc7fec21a6ce171c3bbcb7401a07769.png

⒉示例

首先我们编写一个集合工具类,提供一个方法:

b65f582160a69f4f473d5ccfa5bc1b48.png

0db75086884627978018a846ff365b99.png

可以看到这个方法接收两个参数:

· List list :需要进行转换的集合

· Function :函数接口,接收T类型,返回R类型。用这个函数接口对list中的元素T进行转换,变为R类型

接下来,我们看具体案例:

① 类的静态方法引用

c3a0be1490e11f91cffaa01e9eddfc72.png

我们需要把这个集合中的元素转为十六进制保存,需要调用Integer.toHexString() 方法:

c4bdd15598ac4b4fff57851dc938990d.png

这个方法接收一个 i 类型,返回一个 String 类型,可以用来构造一个 Function 的函数接口:

我们先按照Lambda原始写法,传入的Lambda表达式会被编译为 Function 接口,接口中通过Integer.toHexString(i) 对原来集合的元素进行转换:

be03e2e0ac64c260326c781c2b56679f.png

上面的Lambda表达式代码块中,只有对 Integer.toHexString() 方法的引用,没有其它代码,因此我们可以直接把方法作为参数传递,由编译器帮我们处理,这就是静态方法引用:

ff013fc8d976721e106c709b22374010.png

② 类的非静态方法引用

接下来,我们把刚刚生成的 String 集合 hexList 中的元素都变成大写,需要借助于String类的toUpperCase()方法:

4fbfcece83d8fc927348b795c53cd26a.png

这次是非静态方法,不能用类名调用,需要用实例对象,因此与刚刚的实现有一些差别,我们接收集合中的每一个字符串 s 。但与上面不同然后 s 不是 toUpperCase() 的参数,而是调用者:

733ddd43afb64997f7c1b608f0412266.png

因为代码体只有对 toUpperCase() 的调用,所以可以把方法作为参数引用传递,依然可以简写:

7546ad5b77fbcb8a117f7ba7f13964b3.png

③ 指定实例的非静态方法引用

下面一个需求是这样的,我们先定义一个数字 Integer num = 2000 ,然后用这个数字和集合中的每个数字进行比较,比较的结果放入一个新的集合。比较对象,我们可以用 Integer 的 compareTo 方法:

7e2e3537487630c91feff6bf108be447.png

先用Lambda实现。

31e4b3265fdc4e1c141a5a2bc0fcf397.png

与前面类似,这里Lambda的代码块中,依然只有对 num.compareTo(i) 的调用,所以可以简写。但是,需要注意的是,这次方法的调用者不是集合的元素,而是一个外部的局部变量 num ,因此不能使用Integer::compareTo ,因为这样是无法确定方法的调用者。要指定调用者,需要用  对象::方法名 的方式:

7d961f006412cc7065ff683f42c1c1a1.png

④ 构造函数引用

最后一个场景:把集合中的数字作为毫秒值,构建出 Date 对象并放入集合,这里我们就需要用到Date的构造函数:

d78c3dfdf7f1e3d922f74a77f1b54de4.png

我们可以接收集合中的每个元素,然后把元素作为 Date 的构造函数参数:

ae58dd111347322fe93af43967e5fbc9.png

上面的Lambda表达式实现方式,代码体只有 new Date() 一行代码,因此也可以采用方法引用进行简写。但问题是,构造函数没有名称,我们只能用 new 关键字来代替:

7f803f0c6df13764ff59ae30ecbc544f.png

注意两点:

· 上面代码中的System.out::println 其实是 指定对象System.out的非静态方法println的引用

· 如果构造函数有多个,可能无法区分导致传递失败

接口的默认方法和静态方法

Java 8使用两个新概念扩展了接口的含义:默认方法和静态方法。

⒈默认方法

默认方法使得开发者可以在 不破坏二进制兼容性的前提下,往现存接口中添加新的方法,即不强制那些实现了该接口的类也同时实现这个新加的方法。

默认方法和抽象方法之间的区别在于抽象方法需要实现,而默认方法不需要。接口提供的默认方法会被接口的实现类继承或者覆写,例子代码如下:

49590dbc1ba4762c7e7966402891b2cb.png

97fc5e06786e79141c92eefa554e75b2.png

Defaulable接口使用关键字default定义了一个默认方法notRequired()。DefaultableImpl类实现了这个接口,同时默认继承了这个接口中的默认方法;OverridableImpl类也实现了这个接口,但覆写了该接口的默认方法,并提供了一个不同的实现。

⒉ 静态方法

Java 8带来的另一个有趣的特性是在接口中可以定义静态方法,我们可以直接用接口调用这些静态方法。例子代码如下:

c6c9c814f04fc0d9376ff4f431f12bb9.png

下面的代码片段整合了默认方法和静态方法的使用场景:

9a8d6005c9ba23988ae25f581e93d1b7.png

这段代码的输出结果如下:

6cac6130b7e83d3af72aad0704232f6f.png

由于JVM上的默认方法的实现在字节码层面提供了支持,因此效率非常高。默认方法允许在不打破现有继承体系的基础上改进接口。该特性在官方库中的应用是:给 java.util.Collection 接口添加新方法,如 stream() 、

parallelStream() 、 forEach() 和 removeIf() 等等。

尽管默认方法有这么多好处,但在实际开发中应该谨慎使用:在复杂的继承体系中,默认方法可能引起歧义和编译错误。如果你想了解更多细节,可以参考官方文档。

Optional

Java应用中最常见的bug就是空值异常。

Optional 仅仅是一个容器,可以存放T类型的值或者 null 。它提供了一些有用的接口来避免显式的 null 检查,可以参考Java 8官方文档了解更多细节。

接下来看一点使用Optional的例子:可能为空的值或者某个类型的值:

108ac8b261b14f27160d89ced5caa661.png

如果 Optional 实例持有一个非空值,则 isPresent() 方法返回 true ,否则返回 false ;如果 Optional 实例持有null , orElseGet() 方法可以接受一个lambda表达式生成的默认值;map() 方法可以将现有的 Optional 实例的值转换成新的值;orElse() 方法与 orElseGet() 方法类似,但是在持有null的时候返回传入的默认值,而不是通过Lambda来生成。

上述代码的输出结果如下:

47aa06f5b0546e712426559c096c4bcf.png

再看下另一个简单的例子:

5ba1382edadfd2de2d7f8983f6a4ab4a.png

这个例子的输出是:

4afd051f2f579429ff85637546b30cc7.png

Streams

新增的Stream API(java.util.stream)将生成环境的函数式编程引入了Java库中。这是目前为止最大的一次对Java库的完善,以便开发者能够写出更加有效、更加简洁和紧凑的代码。

Steam API极大得简化了集合操作(后面我们会看到不止是集合),首先看下这个叫Task的类:

db702717f3c6f9a663a01d0f04e3c512.png

Task类有一个points属性,另外还有两种状态:OPEN或者CLOSED。现在假设有一个task集合:

8409d2dcb029080745b68d56d21a7cfb.png

首先看一个问题:在这个task集合中一共有多少个OPEN状态的?计算出它们的points属性和。在Java 8之前,要解决这个问题,则需要使用foreach循环遍历task集合;但是在Java 8中可以利用steams解决:包括一系列元素的列表,并且支持顺序和并行处理。

8914e72b00107d09a97539951b5368bf.png

运行这个方法的控制台输出是:

2136b30ef10cef9f95e6ef26b4b5307b.png

这里有很多知识点值得说。首先, tasks 集合被转换成 steam 表示;其次,在 steam 上的 filter 操作会过滤掉所有CLOSED 的 task ;第三, mapToInt 操作基于 tasks 集合中的每个 task 实例的 Task::getPoints 方法将 task流转换成 Integer 集合;最后,通过 sum 方法计算总和,得出最后的结果。

在学习下一个例子之前,还需要记住一些steams(点此更多细节)的知识点。Steam之上的操作可分为中间操作和晚期操作。

中间操作会返回一个新的steam——执行一个中间操作(例如filter)并不会执行实际的过滤操作,而是创建一个新的steam,并将原steam中符合条件的元素放入新创建的steam。

晚期操作(例如forEach或者sum),会遍历steam并得出结果或者附带结果;在执行晚期操作之后,steam处理线已经处理完毕,就不能使用了。在几乎所有情况下,晚期操作都是立刻对steam进行遍历。

steam的另一个价值是创造性地支持并行处理(parallel processing)。对于上述的tasks集合,我们可以用下面的代码计算所有task的points之和:

d529416f6af164951d11ee79b995aecd.png

这里我们使用parallel方法并行处理所有的task,并使用reduce方法计算最终的结果。控制台输出如下:

71cdfb1b2ed305248a26f62cd58e2445.png

对于一个集合,经常需要根据某些条件对其中的元素分组。利用steam提供的API可以很快完成这类任务,代码如下:

5f8b0fa2c83f836f6e4419a06ab71d5c.png

控制台的输出如下:

d75d24658a640058cf5dcdc87c126d1a.png

最后一个关于tasks集合的例子问题是:如何计算集合中每个任务的点数在集合中所占的比重,具体处理的代码如下:

d05c6b1817dc39c36270e641598c13df.png

控制台输出结果如下:

d50e31a685089ce14f9d06ffce84c871.png

最后,正如之前所说,Steam API不仅可以作用于Java集合,传统的IO操作(从文件或者网络一行一行得读取数据)可以受益于steam处理,这里有一个小例子:

778e86f3930dafc24922a423baaf58d0.png

Stream的方法 onClose() 返回一个等价的有额外句柄的Stream,当Stream的 close() 方法被调用的时候这个句柄会被执行。Stream API、Lambda表达式还有接口默认方法和静态方法支持的方法引用,是Java 8对软件开发的现代范式的响应。

并行数组

Java8版本新增了很多新的方法,用于支持并行数组处理。最重要的方法是 parallelSort() ,可以显著加快多核机器上的数组排序。下面的例子论证了parallexXxx系列的方法:

a4eaafad2f05c29dae9da73c4ee0f4be.png

16f6dcbef14c09640dd600d0b3d0d2f8.png

上述这些代码使用parallelSetAll()方法生成20000个随机数,然后使用parallelSort()方法进行排序。这个程序会输出乱序数组和排序数组的前10个元素。上述例子的代码输出的结果是:

68802e17f25028d3bcb0dc448cac6477.png

热点文章

◆  据说"杨紫"迷上了他,不仅会写代码,还创建了……

◆  RedMonk编程语言排行榜:第一不是Java也不是python!!

◆  程序员收入高、岗位多?可为何还有这么多人找不到工作?

586618c92669fd8b9bee8fe2f2a6e9b0.gif我就知道你会“在看”b406e3142ab7cbc4d72a66b9131ca558.gif

▼点击 加程序员交流群


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部