Java 8 新特性指南
本教程可以在实验楼(shiyanlou.com)中在线练习。
一、实验简介
Java 8是近年来最后起来的一个Java编程语言发行版本。Oracle 在 2014 年 3 月发布了它。该版本为Java带来了许多新特性,是一个具有重大改变的版本。
本课程适用于Java初学者或者是具有一定编程经验的开发者,学完本课程的感觉就像为自己的技能“打了个补丁”。因此在学习之前,仍然建议你具有Java编程基础。
本节内容主要为你讲解Lambda表达式和方法引用两个知识点。
1.1 知识点
Lambda表达式的含义及使用方法
方法引用
1.2 准备工作
常言道:工欲善其事,必先利其器。为了能够正常地利用Java 8带来的特性,我们需要使用JRE 8.0版本(JDK 8.0)作为我们的编译运行环境。实验楼中的Eclipse默认使用JavaSE 1.7,在本实验中我们需要将其修改为JavaSE 1.8。
1.2.1 创建项目
首先请双击桌面上的Eclipse图标,打开Eclipse。然后在菜单中点击File
->New
->Other
。
在弹出的对话框中选择Java Project
,然后点击Next
进入下一步。
在新建项目对话框中填入项目名HelloJava8
,并在下方的JRE设置中选择JavaSE-1.8
。再点击Next
进入下一步。
接着点击Finish
按钮完成项目的创建。
若弹出下面这样的对话框,点击Yes
即可启用相关特性。
1.2.2 项目属性设置
在项目创建完成后,你可能会看到有报错。这是因为当前编译环境并没有加载Java 8相关的库。下面我们来手动设置一下,顺便可以学习如何更改项目所依赖的库。
首先右键点击左侧的项目,然后点击Properities
进入属性设置。
在弹出的属性对话框中,选中左侧的Java Build Path
页,然后在右侧窗口中选择Libraries
选项卡,选中下面报错的库,然后点击Edit
按钮。
在弹出的Edit Library对话框中点击Installed JREs
按钮。
接着选择Standard VM
,点击Next
按钮进入下一步。
在Add JRE对话框中,点击Directory
按钮添加JRE的目录。
现在你需要选中
这个目录。
/usr/lib/jvm/java-8-oracle
随着目录设置的完成,相关的选项已经被加载进来了。点击Finish
按钮完成配置。
回到项目属性对话框,将项目的的JRE设置更改为下面新添加的java-8版本。
返回到上一级的设置,查看Execution environment
是否为JavaSE-1.8。如果是,则点击Finish
关闭对话框。
再点击OK
按钮完成设置。
1.2.3 创建包和类
在项目的scr
目录上点击右键,选择New
->Package
。
在弹出的对话框中填入包名com.shiyanlou.java8
,完成包的创建。
接着在项目目录中的这个包上点击右键,选择New
->Class
。
在弹出的对话框中,填入类名NewFeaturesTester
,完成类的创建。
至此,准备工作就完成了。
二、Lambda表达式
Lambda 表达式是在Java 8中引入的,并且成为了Java 8最大的特点。它使得功能性编程变得非常便利,极大地简化了开发工作。
2.1 语法
一个Lambda表达式具有下面这样的语法特征。它由三个部分组成:第一部分为一个括号内用逗号分隔的形参,参数即函数式接口里面方法的参数;第二部分为一个箭头符号:->
;第三部分为方法体,可以是表达式和代码块。语法如下:
parameter -> expression body
下面列举了Lambda表达式的几个最重要的特征:
可选的类型声明:你不用去声明参数的类型。编译器可以从参数的值来推断它是什么类型。
可选的参数周围的括号:你可以不用在括号内声明单个参数。但是对于很多参数的情况,括号是必需的。
可选的大括号:如果表达式体里面只有一个语句,那么你不必用大括号括起来。
可选的返回关键字:如果表达式体只有单个表达式用于值的返回,那么编译器会自动完成这一步。若要指示表达式来返回某个值,则需要使用大括号。
函数式接口的重要属性是:我们能够使用 Lambda 实例化它们,Lambda 表达式让你能够将函数作为方法参数,或者将代码作为数据对待。Lambda 表达式的引入给开发者带来了不少优点:在 Java 8 之前,匿名内部类,监听器和事件处理器的使用都显得很冗长,代码可读性很差,Lambda 表达式的应用则使代码变得更加紧凑,可读性增强;Lambda 表达式使并行操作大集合变得很方便,可以充分发挥多核 CPU 的优势,更易于为多核处理器编写代码。引用自IBM - Java 8 新特性概述。
2.2 一个Lambda表达式的例子
下面尝试写一些代码来理解Lambda表达式。请在NewFeaturesTester.java
中输入下面这些代码,对于它们的解释在注释中给出。
package com.shiyanlou.java8;
public class NewFeaturesTester {
public static void main(String args[]){
NewFeaturesTester tester = new NewFeaturesTester();
// 带有类型声明的表达式
MathOperation addition = (int a, int b) -> a + b;
// 没有类型声明的表达式
MathOperation subtraction = (a, b) -> a - b;
// 带有大括号、带有返回语句的表达式
MathOperation multiplication = (int a, int b) -> { return a * b; };
// 没有大括号和return语句的表达式
MathOperation division = (int a, int b) -> a / b;
// 输出结果
System.out.println("10 + 5 = " + tester.operate(100, 2, addition));
System.out.println("10 - 5 = " + tester.operate(100, 2, subtraction));
System.out.println("10 x 5 = " + tester.operate(100, 2, multiplication));
System.out.println("10 / 5 = " + tester.operate(100, 2, division));
// 没有括号的表达式
GreetingService greetService1 = message ->
System.out.println("Hello " + message);
// 有括号的表达式
GreetingService greetService2 = (message) ->
System.out.println("Hello " + message);
// 调用sayMessage方法输出结果
greetService1.sayMessage("Shiyanlou");
greetService2.sayMessage("Classmate");
}
// 下面是定义的一些接口和方法
interface MathOperation {
int operation(int a, int b);
}
interface GreetingService {
void sayMessage(String message);
}
private int operate(int a, int b, MathOperation mathOperation){
return mathOperation.operation(a, b);
}
}
接下来我们来编译一下,点击上方工具栏的Run
按钮。
运行结果如下图所示:
需要注意的是:
Lambda表达式优先用于定义功能接口在行内的实现,即单个方法只有一个接口。在上面的例子中,我们用了多个类型的Lambda表达式来定义MathOperation接口的操作方法。然后我们定义了GreetingService的sayMessage的实现。
Lambda表达式让匿名类不再需要,这位Java增添了简洁但实用的函数式编程能力。
2.3 作用域
通过使用Lambda表达式,你可以引用final变量或者有效的final变量(只赋值一次)。如果一个变量被再次赋值,Lambda表达式将抛出一个编译错误。
我们可以通过下面这段代码来学习Lambda的作用域。请将代码修改至如下这些:
package com.shiyanlou.java8;
public class NewFeaturesTester {
final static String salutation = "Hello ";
public static void main(String args[]){
GreetingService greetService1 = message ->
System.out.println(salutation + message);
greetService1.sayMessage("Shiyanlou");
}
interface GreetingService {
void sayMessage(String message);
}
}
点击编译运行,可以看到输出结果如下图所示。
三、方法引用
Java 8中方法也是一种对象,可以By名字来引用。不过方法引用的唯一用途是支持Lambda的简写,使用方法名称来表示Lambda。不能通过方法引用来获得诸如方法签名的相关信息。引用自永无止境,上下求索的博客。
方法引用可以通过方法的名字来引用其本身。方法引用是通过::
符号(双冒号)来描述的。
它可以用来引用下列类型的方法:- 静态方法- 实例方法- 使用new
操作符的构造器方法(TreeSet::new
)
更多对于方法引用的介绍,可以参考这一篇博文——《Java 8之方法引用(Method References)》。
3.1 一个方法引用的例子
请继续修改Eclipse中的代码,学习如何使用方法引用。
package com.shiyanlou.java8;
import java.util.List;
import java.util.ArrayList;
public class NewFeaturesTester {
public static void main(String args[]){
List names = new ArrayList();
names.add("Peter");
names.add("Linda");
names.add("Smith");
names.add("Zack");
names.add("Bob");
// 通过System.out::println引用了输出的方法
names.forEach(System.out::println);
}
}
编译并运行,结果如下图所示:
四、实验总结
本节我们讲解了如何使用Lambda表达式和方法引用。Lambda大概是使用Java 8版本编程最常涉及到的一个技巧,建议在适用的场合多用它代替之前的习惯写法。
Java 8 其他新特性例如函数式接口、默认方法、Optional、Streams(流)、Data/Time API、Base64编码等,可以在实验楼免费查看,实验楼提供了教程配套的在线练习环境:
https://www.shiyanlou.com/courses/539
另外在学习过程中,应当随时保持查阅官方文档的习惯。