Java8新特性及代码示例

Java 8于2014年初发布。本文列出了重要的 Java 8 特性,并提供了 lambda 表达式、Java 流、函数式接口、接口默认方法和日期时间 API 更改等功能的代码示例。
1. Lambda表达式 使用Scala的同学已经很熟悉Lambda表达式了。在Java编程语言中,Lambda表达式只是一个匿名函数,就是没有名称并且没有任何修饰符的函数。
1.1 lambda语法
lambda 表达式的基本语法:

//如果 lambda 表达式的正文具有单个语句,则可以不要大括号 //并且匿名函数的返回类型与正文表达式的返回类型相同。 (parameters) -> expression // 当正文中有多个语句时,这些语句必须括在大括号中。 (parameters) -> { statements; } //空括号用于表示一组空参数 () -> expression

例如:
//这个lambda表达式接收两个参数并返回它们的和 (x, y) -> x + y

上面这个例子可以作为两个整型数字相加,也可以用于连接两个字符串,Java会根据上下文自动进行类型匹配。
1.2 lambda编写规则
  • lambda 表达式可以有零个、一个或多个参数。
  • 参数的类型可以显式声明,也可以从上下文中推断出来。
  • 多个参数括在小括号中,并用逗号分隔。空括号用于表示一组空参数。
  • 当只有单个参数时,如果推断出其类型,则不必使用括号。
  • lambda 表达式的主体可以包含零个、一个或多个语句。
  • 如果 lambda 表达式的正文具有单个语句,则可以不要大括号,并且匿名函数的返回类型与正文表达式的返回类型相同。当正文中有多个语句时,这些语句必须括在大括号中。
2. 函数式接口 函数式接口就是只有一个抽象方法的接口,接口只允许有一个抽象方法。
Java 8引入了新的注解@FunctionalInterface,我们可以使用它来给出编译时错误,检验功能接口是否违反了规约。
2.1 示例
//可选注解,也可以不写注解 @FunctionalInterface public interface MyFirstFunctionalInterface { public void onlyOneMethod(); }

即使省略注释@FunctionalInterface,函数式接口也是有效的。它仅用于通知编译器在接口内强制实施单个抽象方法。
另外,因为默认方法不是抽象方法,其有一个默认实现,所以是符合函数式接口的定义的。
要记住的另一个关键点是,如果函数接口覆盖了基类(java.lang.Object)的公共方法之一,则该方法也不计入接口的抽象方法总数,因为该接口的任何实现都将具有来自(java.lang.Object)或其他地方的实现。
下面给出的是一个完全有效的功能接口:
@FunctionalInterface public interface MyFirstFunctionalInterface { public void onlyOneMethod(); @Override public String toString(); @Override public boolean equals(Object obj); }

3. 默认方法 Java 8允许我们在接口中添加非抽象方法。这些方法必须声明为default方法。Java 8 中引入默认方法,以便更好的使用 lambda 表达式的功能。
默认方法使我们能够为库的接口引入新功能,并确保与为这些接口的旧版本编写的代码的二进制兼容性。
让我们通过一个例子来理解:
public interface Moveable { default void move(){ System.out.println("I am moving"); } }

Moveable接口定义了一个move()方法,并提供了一个默认的实现。如果任何类实现此接口,则它不需要实现自己的move()方法,它可以直接调用实例后的move()方法。
例如:
public class Animal implements Moveable{ public static void main(String[] args){ Animal tiger = new Animal(); tiger.move(); } } //打印:I am moving

如果类愿意自定义move()方法的行为,那么它可以提供自己的自定义实现并重写该move()方法。
4. Java流 Java8另一个重大变化是引入了流,它提供了一种机制,用于以各种方式处理数据,包括过滤、转换或可能对应用程序有用的任何其他方式。
Java 8 中的 流(Streams) API 支持不同类型的迭代,在该迭代中,我们定义了要处理的项集、要对每个项执行的操作以及要存储这些操作的输出位置。
4.1 示例
在下面这个示例中,我们想要删除前缀有str的数据:
package cn.dayangshuo; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; /** * @author DAYANG */ public class StreamTest { public static void main(String[] args) { List items = Arrays.asList("str123", "dayang", "大阳"); String prefix = "str"; List filteredList = items.stream() .filter(e -> (!e.startsWith(prefix))) .collect(Collectors.toList()); filteredList.forEach(System.out::println); } } //打印内容: //dayang //大阳

5. Java8时间/日期API更改 新的日期和时间 API/类 (JSR-310),也称为 ThreeTen,只是改变了我们在 java 应用程序中处理日期的方式。
5.1 日期类/时区类
Data类已经过时了。用于替换 Date 类的新类是 LocalDate、LocalTime和LocalDateTime
  • LocalDate:表示一个日期,没有时间或时区的信息
  • LocalTime:表示一个时间,没有日期或时区的信息
  • LocalDateTime表示日期和时间,没有时区信息
如果我们想将日期功能与时区信息一起使用,那么 Lambda 为我们提供了额外的三个类,类似于上面的一个类,即OffsetDate、OffsetTime 和OffsetDateTime。
示例:
//此刻日期 LocalDate localDate = LocalDate.now(); //时间 LocalTime localTime = LocalTime.of(12, 20); //此刻日期时间 LocalDateTime localDateTime = LocalDateTime.now(); //2017-03-13T11:28:29.247+08:00:东八区时间 OffsetDateTime offsetDateTime = OffsetDateTime.now(); //2017-03-13T04:28:29.248+01:00[Europe/Paris]:欧洲巴黎时间 ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of("Europe/Paris"));

5.2 时间戳和持续时间类
为了表示任何时刻的特定时间戳,需要使用的类是 Instant。该类表示精确度为纳秒的瞬间。
示例:对Instant与另一个Instant进行比较的操作,并添加或减去一些时间
//2017-03-13T03:38:57.134Z Instant instant = Instant.now(); //2017-03-13T03:38:57.134Z Instant instant1 = instant.plus(Duration.ofMillis(5000)); //2017-03-13T03:38:47.134Z Instant instant2 = instant.minus(Duration.ofMillis(5000)); //2017-03-13T03:38:42.134Z Instant instant3 = instant.minusSeconds(10);

Duration类(持续时间)是Java8中首次出现的全新概念。它表示两个时间戳之间的时间差。Duration处理一小段时间单位,如毫秒、秒、分钟和小时。它们更适合与应用程序代码交互。
Duration duration = Duration.ofMillis(5000); duration = Duration.ofSeconds(60); duration = Duration.ofMinutes(10);

【Java8新特性及代码示例】处理更大的时间间隔,可以使用Period
Period period = Period.ofDays(6); period = Period.ofMonths(6); period = Period.between(LocalDate.now(), LocalDate.now().plusDays(60));

    推荐阅读