Java 8 新特性指南(一)

本课程可以在实验楼中在线完成,立即【开始实验】。

一、实验简介

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版本编程最常涉及到的一个技巧,建议在适用的场合多用它代替之前的习惯写法。

在学习过程中,应当随时保持查阅官方文档的习惯。

下面给出了一些你可能用到的链接:

本课程可以在实验楼中在线完成,立即【开始实验】。

石头山

继续阅读此作者的更多文章