Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本。 Oracle 公司于 2014 年 3 月 18 日发布 Java 8 ,它支持函数式编程,新的 JavaScript 引擎,新的日期 API,新的Stream API 等。javascript
Java8 新增了很是多的特性,咱们主要讨论如下几个:java
接下来咱们将详细为你们简介 Java 8 的新特性:程序员
Lambda 表达式,也可称为闭包,它是推进 Java 8 发布的最重要新特性。
Lambda 容许把函数做为一个方法的参数(函数做为参数传递进方法中)。
使用 Lambda 表达式可使代码变的更加简洁紧凑。sql
lambda 表达式的语法格式以下:shell
(parameters) -> expression 或 (parameters) ->{ statements; }
如下是lambda表达式的重要特征:数据库
Lambda 表达式的简单例子:express
//零个 () -> System.out.println("no argument"); //一个 x -> x + 1 //两个 (x,y) -> x + y //省略参数类型 View.OnClickListener oneArgument = view -> Log.d(TAG, "one argument"); //指定参数类型 View.OnClickListener oneArgument = (View view) -> Log.d(TAG, "one argument"); //多行语句 //返回类型是代码块返回的void View.OnClickListener multiLine = (View view) -> { Log.d(TAG,"multi statements"); Log.d(TAG,"second line"); } //返回类型是表达式主体语句的返回类型int (int x)-> x + 1
使用 Lambda 表达式须要注意如下两点:编程
lambda 表达式只能引用标记了 final 的外层局部变量,这就是说不能在 lambda 内部修改定义在域外的局部变量,不然会编译错误。数组
方法引用经过方法的名字来指向一个方法。
方法引用可使语言的构造更紧凑简洁,减小冗余代码。
方法引用使用一对冒号 类名::方法名 。安全
下面在 Car 类中定义了 4 个方法做为例子来区分 Java 中 4 种不一样方法的引用。
package com.runoob.main; @FunctionalInterface public interface Supplier<T> { T get(); } class Car { //Supplier是jdk1.8的接口,这里和lamda一块儿使用了 public static Car create(final Supplier<Car> supplier) { return supplier.get(); } public static void collide(final Car car) { System.out.println("Collided " + car.toString()); } public void follow(final Car another) { System.out.println("Following the " + another.toString()); } public void repair() { System.out.println("Repaired " + this.toString()); } }
ClassName::new
ContainingClass::staticMethodName
ContainingType::methodName
ContainingObject::instanceMethodName
函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,可是能够有多个非抽象方法的接口。
函数式接口能够被隐式转换为 lambda 表达式。
Lambda 表达式和方法引用(实际上也可认为是Lambda表达式)上。
如定义了一个函数式接口以下:
@FunctionalInterface interface GreetingService { void sayMessage(String message); }
那么就可使用Lambda表达式来表示该接口的一个实现(注:JAVA 8 以前通常是用匿名类实现的):
GreetingService greetService1 = message -> System.out.println("Hello " + message);
函数式接口能够对现有的函数友好地支持 lambda。
JDK 1.8 以前已有的函数式接口:
JDK 1.8 新增长的函数接口:
java.util.function 包含了不少类,用来支持 Java的 函数式编程,该包中的函数式接口有:
序号 | 接口 & 描述 | |
---|---|---|
1 | BiConsumer<T,U> | 表明了一个接受两个输入参数的操做,而且不返回任何结果 |
2 | BiFunction<T,U,R> | 表明了一个接受两个输入参数的方法,而且返回一个结果 |
3 | BinaryOperator<T> | 表明了一个做用于于两个同类型操做符的操做,而且返回了操做符同类型的结果 |
4 | BiPredicate<T,U> | 表明了一个两个参数的boolean值方法 |
5 | Boolean Supplier | 表明了boolean值结果的提供方 |
6 | Consumer<T> | 表明了接受一个输入参数而且无返回的操做 |
7 | Double BinaryOperator | 表明了做用于两个double值操做符的操做,而且返回了一个double值的结果。 |
8 | Double Consumer | 表明一个接受double值参数的操做,而且不返回结果。 |
9 | Double Function<R> | 表明接受一个double值参数的方法,而且返回结果 |
10 | Double Predicate | 表明一个拥有double值参数的boolean值方法 |
11 | Double Supplier | 表明一个double值结构的提供方 |
12 | Double ToIntFunction | 接受一个double类型输入,返回一个int类型结果。 |
13 | Double ToLongFunction | 接受一个double类型输入,返回一个long类型结果 |
14 | Double UnaryOperator | 接受一个参数同为类型double,返回值类型也为double 。 |
15 | Function<T,R> | 接受一个输入参数,返回一个结果。 |
16 | IntBinaryOperator | 接受两个参数同为类型int,返回值类型也为int 。 |
17 | IntConsumer | 接受一个int类型的输入参数,无返回值 。 |
18 | IntFunction<R> | 接受一个int类型输入参数,返回一个结果 。 |
19 | IntPredicate | 接受一个int输入参数,返回一个布尔值的结果。 |
20 | IntSupplier | 无参数,返回一个int类型结果。 |
21 | IntToDoubleFunction | 接受一个int类型输入,返回一个double类型结果 。 |
22 | IntToLongFunction | 接受一个int类型输入,返回一个long类型结果。 |
23 | IntUnaryOperator | 接受一个参数同为类型int,返回值类型也为int 。 |
24 | LongBinaryOperator | 接受两个参数同为类型long,返回值类型也为long。 |
25 | LongConsumer | 接受一个long类型的输入参数,无返回值。 |
26 | LongFunction<R> | 接受一个long类型输入参数,返回一个结果。 |
27 | LongPredicate | R接受一个long输入参数,返回一个布尔值类型结果。 |
28 | LongSupplier | 无参数,返回一个结果long类型的值。 |
29 | LongToDoubleFunction | 接受一个long类型输入,返回一个double类型结果。 |
30 | LongToIntFunction | 接受一个long类型输入,返回一个int类型结果。 |
31 | LongUnaryOperator | 接受一个参数同为类型long,返回值类型也为long。 |
32 | ObjDoubleConsumer<T> | 接受一个object类型和一个double类型的输入参数,无返回值。 |
33 | ObjIntConsumer<T> | 接受一个object类型和一个int类型的输入参数,无返回值。 |
34 | ObjLongConsumer<T> | 接受一个object类型和一个long类型的输入参数,无返回值。 |
35 | Predicate<T> | 接受一个输入参数,返回一个布尔值结果。 |
36 | Supplier<T> | 无参数,返回一个结果。 |
37 | ToDoubleBiFunction<T,U> | 接受两个输入参数,返回一个double类型结果 |
38 | ToDoubleFunction<T> | 接受一个输入参数,返回一个double类型结果 |
39 | ToIntBiFunction<T,U> | 接受两个输入参数,返回一个int类型结果。 |
40 | ToIntFunction<T> | 接受一个输入参数,返回一个int类型结果。 |
41 | ToLongBiFunction<T,U> | 接受两个输入参数,返回一个long类型结果。 |
42 | ToLongFunction<T> | 接受一个输入参数,返回一个long类型结果。 |
43 | UnaryOperator<T> | 接受一个参数为类型T,返回值类型也为T。 |
Java 8 新增了接口的默认方法。
简单说,默认方法就是接口能够有实现方法,并且不须要实现类去实现其方法。
咱们只需在方法名前面加个default关键字便可实现默认方法。
为何要有这个特性?
首先,以前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当须要修改接口时候,须要修改所有实现该接口的类,目前的java 8以前的集合框架没有foreach方法,一般能想到的解决办法是在JDK里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是无法在给接口添加新方法的同时不影响已有的实现。因此引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。
语法
默认方法语法格式以下:
public interface Vehicle { default void print(){ System.out.println("我是一辆车!"); } }
多个默认方法
public interface Vehicle { default void print(){ System.out.println("我是一辆车!"); } } public interface FourWheeler { default void print(){ System.out.println("我是一辆四轮车!"); } }
public class Car implements Vehicle, FourWheeler { default void print(){ System.out.println("我是一辆四轮汽车!"); } }
public class Car implements Vehicle, FourWheeler { public void print(){ Vehicle.super.print(); } }
Java 8 的另外一个特性是接口能够声明(而且能够提供实现)静态方法。例如:
public interface Vehicle { default void print(){ System.out.println("我是一辆车!"); } // 静态方法 static void blowHorn(){ System.out.println("按喇叭!!!"); } }
Java 8 API添加了一个新的抽象称为流Stream,可让你以一种声明的方式处理数据。
Stream 使用一种相似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream API能够极大提升Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看做一种流, 流在管道中传输, 而且能够在管道的节点上进行处理, 好比筛选, 排序,聚合等。
元素流在管道中通过中间操做(intermediate operation)的处理,最后由最终操做(terminal operation)获得前面处理的结果。
+--------------------+ +------+ +------+ +---+ +-------+ | stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect| +--------------------+ +------+ +------+ +---+ +-------+
以上的流程转换为 Java 代码为:
List<Integer> transactionsIds = widgets.stream() .filter(b -> b.getColor() == RED) .sorted((x,y) -> x.getWeight() - y.getWeight()) .mapToInt(Widget::getWeight) .sum();
Stream(流)是一个来自数据源的元素队列并支持聚合操做
和之前的Collection操做不一样, Stream操做还有两个基础的特征:
在 Java 8 中, 集合接口有两个方法来生成流:
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
forEach
Stream 提供了新的方法 'forEach' 来迭代流中的每一个数据。如下代码片断使用 forEach 输出了10个随机数:
Random random = new Random(); random.ints().limit(10).forEach(System.out::println);
map
map 方法用于映射每一个元素到对应的结果,如下代码片断使用 map 输出了元素对应的平方数:
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); // 获取对应的平方数 List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
filter
filter 方法用于经过设置的条件过滤出元素。如下代码片断使用 filter 方法过滤出空字符串:
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); // 获取空字符串的数量 int count = strings.stream().filter(string -> string.isEmpty()).count();
limit
limit 方法用于获取指定数量的流。 如下代码片断使用 limit 方法打印出 10 条数据:
Random random = new Random(); random.ints().limit(10).forEach(System.out::println);
sorted
sorted 方法用于对流进行排序。如下代码片断使用 sorted 方法对输出的 10 个随机数进行排序:
Random random = new Random(); random.ints().limit(10).sorted().forEach(System.out::println);
parallel(并行)程序
parallelStream 是流并行处理程序的代替方法。如下实例咱们使用 parallelStream 来输出空字符串的数量:
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); // 获取空字符串的数量 int count = strings.parallelStream().filter(string -> string.isEmpty()).count();
Collectors
Collectors 类实现了不少归约操做,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串:
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList()); System.out.println("筛选列表: " + filtered); String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(",")); System.out.println("合并字符串: " + mergedString);
统计
另外,一些产生统计结果的收集器也很是有用。它们主要用于int、double、long等基本类型上,它们能够用来产生相似以下的统计结果。
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics(); System.out.println("列表中最大的数 : " + stats.getMax()); System.out.println("列表中最小的数 : " + stats.getMin()); System.out.println("全部数之和 : " + stats.getSum()); System.out.println("平均数 : " + stats.getAverage());
Optional 类是一个能够为null的容器对象。若是值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional 是个容器:它能够保存类型T的值,或者仅仅保存null。Optional提供不少有用的方法,这样咱们就不用显式进行空值检测。
Optional 类的引入很好的解决空指针异常。
如下是一个 java.util.Optional<T> 类的声明:
public final class Optional<T> extends Object
序号 | 方法 & 描述 | |
---|---|---|
1 | static <T> Optional<T> empty() | 返回空的 Optional 实例。 |
2 | boolean equals(Object obj) | 判断其余对象是否等于 Optional。 |
3 | Optional<T> filter(Predicate<? super <T> predicate) | 若是值存在,而且这个值匹配给定的 predicate,返回一个Optional用以描述这个值,不然返回一个空的Optional。 |
4 | <U> Optional<U> flatMap(Function<? super T,Optional<U>> mapper) | 若是值存在,返回基于Optional包含的映射方法的值,不然返回一个空的Optional。 |
5 | T get() | 若是在这个Optional中包含这个值,返回值,不然抛出异常:NoSuchElementException。 |
6 | int hashCode() | 返回存在值的哈希码,若是值不存在 返回 0。 |
7 | void ifPresent(Consumer<? super T> consumer) | 若是值存在则使用该值调用 consumer , 不然不作任何事情。 |
8 | boolean isPresent() | 若是值存在则方法会返回true,不然返回 false。 |
9 | <U>Optional<U> map(Function<? super T,? extends U> mapper) | 若是有值,则对其执行调用映射函数获得返回值。若是返回值不为 null,则建立包含映射返回值的Optional做为map方法返回值,不然返回空Optional。 |
10 | static <T> Optional<T> of(T value) | 返回一个指定非null值的Optional。 |
11 | static <T> Optional<T> ofNullable(T value) | 若是为非空,返回 Optional 描述的指定值,不然返回空的 Optional。 |
12 | T orElse(T other) | 若是存在该值,返回值, 不然返回 other。 |
13 | T orElseGet(Supplier<? extends T> other) | 若是存在该值,返回值, 不然触发 other,并返回 other 调用的结果。 |
14 | <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) | 若是存在该值,返回包含的值,不然抛出由 Supplier 继承的异常 |
15 | String toString() | 返回一个Optional的非空字符串,用来调试 |
[注意]:这些方法是从 java.lang.Object 类继承来的。
Nashorn 一个 javascript 引擎。
从JDK 1.8开始,Nashorn取代Rhino(JDK 1.6, JDK1.7)成为Java的嵌入式JavaScript引擎。Nashorn彻底支持ECMAScript 5.1规范以及一些扩展。它使用基于JSR 292的新语言特性,其中包含在JDK 7中引入的 invokedynamic,将JavaScript编译成Java字节码。
与先前的Rhino实现相比,这带来了2到10倍的性能提高。
jjs是个基于Nashorn引擎的命令行工具。它接受一些JavaScript源代码为参数,而且执行这些源代码。
例如,咱们建立一个具备以下内容的sample.js文件:
print('Hello World!');
打开控制台,输入如下命令:
$ jjs sample.js
以上程序输出结果为:
Hello World!
jjs 交互式编程
打开控制台,输入如下命令:
$ jjs jjs> print("Hello, World!") Hello, World! jjs> quit() >>
传递参数
打开控制台,输入如下命令:
$ jjs -- a b c jjs> print('字母: ' +arguments.join(", ")) 字母: a, b, c jjs>
使用 ScriptEngineManager, JavaScript 代码能够在 Java 中执行,实例以下:
import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; public class Java8Tester { public static void main(String[] args) { ScriptEngineManager scriptEngineManager = new ScriptEngineManager(); ScriptEngine nashorn = scriptEngineManager.getEngineByName("nashorn"); String name = "Runoob"; Integer result = null; try { nashorn.eval("print('" + name + "')"); result = (Integer) nashorn.eval("10 + 2"); } catch (ScriptException e) { System.out.println("执行脚本错误: " + e.getMessage()); } System.out.println(result.toString()); } }
执行以上脚本,输出结果为:
$ javac Java8Tester.java $ java Java8Tester Runoob 12
如下实例演示了如何在 JavaScript 中引用 Java 类:
var BigDecimal = Java.type('java.math.BigDecimal'); function calculate(amount, percentage) { var result = new BigDecimal(amount).multiply( new BigDecimal(percentage)).divide(new BigDecimal("100"), 2, BigDecimal.ROUND_HALF_EVEN); return result.toPlainString(); } var result = calculate(568000000000000000023,13.9); print(result);
咱们使用 jjs 命令执行以上脚本,输出结果以下:
$ jjs sample.js 78952000000000002017.94
Java 8经过发布新的Date-Time API (JSR 310)来进一步增强对日期与时间的处理。
在旧版的 Java 中,日期时间 API 存在诸多问题,其中有:
Java 8 在 java.time 包下提供了不少新的 API。如下为两个比较重要的 API:
新的java.time包涵盖了全部处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操做。
LocalDate/LocalTime 和 LocalDateTime 类能够在处理时区不是必须的状况。
import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.Month; public class Java8Tester { public static void main(String[] args) { Java8Tester java8tester = new Java8Tester(); java8tester.testLocalDateTime(); } public void testLocalDateTime() { // 获取当前的日期时间 LocalDateTime currentTime = LocalDateTime.now(); System.out.println("当前时间: " + currentTime); LocalDate date1 = currentTime.toLocalDate(); System.out.println("date1: " + date1); Month month = currentTime.getMonth(); int day = currentTime.getDayOfMonth(); int seconds = currentTime.getSecond(); System.out.println("月: " + month + ", 日: " + day + ", 秒: " + seconds); LocalDateTime date2 = currentTime.withDayOfMonth(10).withYear(2012); System.out.println("date2: " + date2); // 12 december 2014 LocalDate date3 = LocalDate.of(2014, Month.DECEMBER, 12); System.out.println("date3: " + date3); // 22 小时 15 分钟 LocalTime date4 = LocalTime.of(22, 15); System.out.println("date4: " + date4); // 解析字符串 LocalTime date5 = LocalTime.parse("20:15:30"); System.out.println("date5: " + date5); } }
若是咱们须要考虑到时区,就可使用时区的日期时间API:
import java.time.ZoneId; import java.time.ZonedDateTime; public class Java8Tester { public static void main(String[] args) { Java8Tester java8tester = new Java8Tester(); java8tester.testZonedDateTime(); } public void testZonedDateTime() { // 获取当前时间日期 ZonedDateTime date1 = ZonedDateTime.parse( "2015-12-03T10:15:30+05:30[Asia/Shanghai]"); System.out.println("date1: " + date1); ZoneId id = ZoneId.of("Europe/Paris"); System.out.println("ZoneId: " + id); ZoneId currentZone = ZoneId.systemDefault(); System.out.println("当期时区: " + currentZone); } }
在Java 8中,Base64编码已经成为Java类库的标准。
Java 8 内置了 Base64 编码的编码规则和解码规则。
Base64工具类提供了一套静态方法获取下面三种BASE64编解码对象:
序号 | 内嵌类 & 描述 | |
---|---|---|
1 | static class Base64.Decoder | 该类实现一个解码对象用于,使用 Base64 编码来解码字节数据。 |
2 | static class Base64.Encoder | 该类实现一个编码对象,使用 Base64 编码来编码字节数据。 |
序号 | 方法名 & 描述 | |
---|---|---|
1 | static Base64.Decoder getDecoder() | 返回一个 Base64.Decoder ,解码使用基本型 base64 编码方案。 |
2 | static Base64.Encoder getEncoder() | 返回一个 Base64.Encoder ,编码使用基本型 base64 编码方案。 |
3 | static Base64.Decoder getMimeDecoder() | 返回一个 Base64.Decoder ,解码使用 MIME 型 base64 编码方案。 |
4 | static Base64.Encoder getMimeEncoder() | 返回一个 Base64.Encoder ,编码使用 MIME 型 base64 编码方案。 |
5 | static Base64.Encoder getMimeEncoder(int lineLength, byte[] lineSeparator) | 返回一个 Base64.Encoder ,编码使用 MIME 型 base64 编码方案,能够经过参数指定每行的长度及行的分隔符。 |
6 | static Base64.Decoder getUrlDecoder() | 返回一个 Base64.Decoder ,解码使用 URL 和文件名安全型 base64 编码方案。 |
7 | static Base64.Encoder getUrlEncoder() | 返回一个 Base64.Encoder ,编码使用 URL 和文件名安全型 base64 编码方案。 |
[注意]:Base64 类的不少方法从 java.lang.Object 类继承。