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 類繼承。