java 8的新特性(全)
Lambda表示式–允許把函式作為一個方法的引數傳遞進去;
方法引用--可以直接引用已有Java類或者物件(例項)的方法或者構造器。與lambda聯合使用,方法引用可以使語言的構造更緊湊簡潔,減少冗餘程式碼。
函式式介面–函式式介面(FunctionalInterface)就是一個有且僅有一個抽象方法,但是可以有多個非抽象方法的介面。函式式介面可以被隱式轉換為lambda表示式。函式式介面可以現有的函式友好地支援 lambda。
預設方法 − 預設方法就是一個在接口裡面有了一個實現的方法。
新工具 − 新的編譯工具,如:Nashorn引擎 jjs、 類依賴分析器jdeps。
Stream API −新新增的Stream API(java.util.stream) 把真正的函數語言程式設計風格引入到Java中。
Date Time API − 加強對日期與時間的處理。
Optional 類 − Optional 類已經成為 Java 8 類庫的一部分,用來解決空指標異常。
Nashorn, JavaScript 引擎 − Java 8提供了一個新的Nashorn javascript引擎,它允許我們在JVM上執行特定的javascript應用。
Lambda表示式:
lambda 表示式的語法格式如下:
(parameters) -> expression
或
(parameters) ->{ statements; }
以下是lambda表示式的重要特徵:
可選型別宣告:不需要宣告引數型別,編譯器可以統一識別引數值。
可選的引數圓括號:一個引數無需定義圓括號,但多個引數需要定義圓括號。
可選的大括號:如果主體包含了一個語句,就不需要使用大括號。
可選的返回關鍵字:如果主體只有一個表示式返回值則編譯器會自動返回值,大括號需要指定明表示式返回了一個數值。
lambda 表示式的區域性變數一般都是final修飾的變數,也可以不用宣告為 final,但是必須不可被後面的程式碼修改(即隱性的具有 final 的語義)。
方法引用
方法引用使用一對冒號 ::
方法引用通過方法的名字來指向一個方法。
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()); } }
針對上述程式碼例子,我們來學習一下方法引用的幾種形式:
- 構造器引用:它的語法是Class::new,或者更一般的Class< T >::new例項如下:
- 靜態方法引用以及特定類的任意物件的方法引用:它的語法是Class::static_method\Class::method,例項如下:
cars.forEach( Car::collide );//static method
cars.forEach( Car::repair );//method
- 特定物件的方法引用:它的語法是instance::method例項如下:
final Car police = Car.create( Car::new );
cars.forEach( police::follow );
預設方法
預設方法就是介面可以有實現方法,而且不需要實現類去實現其方法。介面提供的預設方法會被介面的實現類繼承或者覆寫。
我們只需在方法名前面加個default關鍵字即可實現預設方法。
靜態預設方法
Java 8 的另一個特性是介面可以宣告(並且可以提供實現)靜態方法。
public interface Vehicle {
default void print(){
System.out.println("我是一輛車!");
}
// 靜態方法
static void blowHorn(){
System.out.println("按喇叭!!!");
}
}
函式式介面
函式式介面–函式式介面(FunctionalInterface)就是一個有且僅有一個抽象方法,但是可以有多個非抽象方法的介面。函式式介面可以被隱式轉換為lambda表示式。函式式介面可以現有的函式友好地支援 lambda。
如定義了一個函式式介面如下:
@FunctionalInterface
interface GreetingService
{
void sayMessage(String message);
}
就可以使用Lambda表示式來表示該介面的一個實現(注:JAVA 8 之前一般是用匿名類實現的):
GreetingService greetService1 = message -> System.out.println("Hello " + message);
JDK 1.8 新增加的函式介面:java.util.function
Predicate 介面是一個函式式介面,它接受一個輸入引數 T,返回一個布林值結果。
該介面包含多種預設方法來將Predicate組合成其他複雜的邏輯(比如:與,或,非)。
該介面用於測試物件是 true 或 false。
例項:
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class Java8Tester {
public static void main(String args[]){
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
// Predicate<Integer> predicate = n -> true
// n 是一個引數傳遞到 Predicate 介面的 test 方法
// n 如果存在則 test 方法返回 true
System.out.println("輸出所有資料:");
// 傳遞引數 n
eval(list, n->true);
// Predicate<Integer> predicate1 = n -> n%2 == 0
// n 是一個引數傳遞到 Predicate 介面的 test 方法
// 如果 n%2 為 0 test 方法返回 true
System.out.println("輸出所有偶數:");
eval(list, n-> n%2 == 0 );
// Predicate<Integer> predicate2 = n -> n > 3
// n 是一個引數傳遞到 Predicate 介面的 test 方法
// 如果 n 大於 3 test 方法返回 true
System.out.println("輸出大於 3 的所有數字:");
eval(list, n-> n > 3 );
}
public static void eval(List<Integer> list, Predicate<Integer> predicate) {
for(Integer n: list) {
if(predicate.test(n)) {
System.out.println(n + " ");
}
}
}
}
Stream
Java 8 API添加了一個新的抽象稱為流Stream,可以讓你以一種宣告的方式處理資料。
Stream 使用一種類似用 SQL 語句從資料庫查詢資料的直觀方式來提供一種對 Java 集合運算和表達的高階抽象。
Stream API可以極大提高Java程式設計師的生產力,讓程式設計師寫出高效率、乾淨、簡潔的程式碼。
這種風格將要處理的元素集合看作一種流, 流在管道中傳輸, 並且可以在管道的節點上進行處理, 比如篩選, 排序,聚合等。
元素流在管道中經過中間操作(intermediate operation)的處理,最後由最終操作(terminal operation)得到前面處理的結果。
List<Integer> transactionsIds =
widgets.stream()
.filter(b -> b.getColor() == RED)
.sorted((x,y) -> x.getWeight() - y.getWeight())
.mapToInt(Widget::getWeight)
.sum();
Stream中提供了許多新方法(map,filter、limit、sorted、collector、統計等等),具體詳見http://www.runoob.com/java/java8-streams.html
parallelStream 是流並行處理程式的代替方法。以下例項我們使用parallelStream 來輸出空字串的數量:
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
// 獲取空字串的數量
int count = (int) strings.parallelStream().filter(string -> string.isEmpty()).count();
Optional 類
Optional類是一個可以為null的容器物件。如果值存在則isPresent()方法會返回true,呼叫get()方法會返回該物件。
Optional 是個容器:它可以儲存型別T的值,或者僅僅儲存null。Optional提供很多有用的方法,這樣我們就不用顯式進行空值檢測。
Optional 類的引入很好的解決空指標異常。
宣告:
public final class Optional<T>
extends Object
該類中有許多方法:
Date Time API
Java 8通過釋出新的Date-Time API (JSR 310)來進一步加強對日期與時間的處理。
在舊版的 Java 中,日期時間 API 存在諸多問題,其中有:
1.非執行緒安全 − java.util.Date 是非執行緒安全的,所有的日期類都是可變的,這是Java日期類最大的問題之一。
2.設計很差 − Java的日期/時間類的定義並不一致,在java.util和java.sql的包中都有日期類,此外用於格式化和解析的類在java.text包中定義。java.util.Date同時包含日期和時間,而java.sql.Date僅包含日期,將其納入java.sql包並不合理。另外這兩個類都有相同的名字,這本身就是一個非常糟糕的設計。
3.時區處理麻煩 − 日期類並不提供國際化,沒有時區支援,因此Java引入了java.util.Calendar和java.util.TimeZone類,但他們同樣存在上述所有的問題。
Java 8 在 java.time 包下提供了很多新的 API。以下為兩個比較重要的 API:
1.Local(本地) − 簡化了日期時間的處理,沒有時區的問題。
2.Zoned(時區) − 通過制定的時區處理日期時間。
新的java.time包涵蓋了所有處理日期,時間,日期/時間,時區,時刻(instants),過程(during)與時鐘(clock)的操作。
LocalDate/LocalTime 和 LocalDateTime 類可以在處理時區不是必須的情況。程式碼如下:
Java8Tester.java 檔案
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.LocalDateTime;
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);
}
}
執行以上指令碼,輸出結果為:
$ javac Java8Tester.java
$ java Java8Tester
當前時間: 2016-04-15T16:55:48.668
date1: 2016-04-15
月: APRIL, 日: 15, 秒: 48
date2: 2012-04-10T16:55:48.668
date3: 2014-12-12
date4: 22:15
date5: 20:15:30
使用時區的日期時間API
如果我們需要考慮到時區,就可以使用時區的日期時間API:
Java8Tester.java 檔案
import java.time.ZonedDateTime;
import java.time.ZoneId;
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);
}
}
執行以上指令碼,輸出結果為:
$ javac Java8Tester.java
$ java Java8Tester
date1: 2015-12-03T10:15:30+08:00[Asia/Shanghai]
ZoneId: Europe/Paris
當期時區: Asia/Shanghai
Nashorn
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
jjs是個基於Nashorn引擎的命令列工具。它接受一些JavaScript原始碼為引數,並且執行這些原始碼。
Java 中呼叫 JavaScript
使用 ScriptEngineManager, JavaScript 程式碼可以在 Java 中執行,例項如下:
Java8Tester.java 檔案
import javax.script.ScriptEngineManager;
import javax.script.ScriptEngine;
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
以下例項演示瞭如何在 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
Base64編碼
在Java 8中,Base64編碼已經成為Java類庫的標準。
Java 8 內建了 Base64 編碼的編碼器和解碼器。
Base64工具類提供了一套靜態方法獲取下面三種BASE64編解碼器:
1.基本:輸出被對映到一組字元A-Za-z0-9+/,編碼不新增任何行標,輸出的解碼僅支援A-Za-z0-9+/。
2.URL:輸出對映到一組字元A-Za-z0-9+_,輸出是URL和檔案。
3.MIME:輸出隱射到MIME友好格式。輸出每行不超過76字元,並且使用’\r’並跟隨’\n’作為分割。編碼輸出最後沒有行分割。
Base64編碼例項見:http://www.runoob.com/java/java8-base64.html
其他特性
重複註解
在Java 5中使用註解有一個限制,即相同的註解在同一位置只能宣告一次。Java 8引入重複註解,這樣相同的註解在同一地方也可以宣告多次。重複註解機制本身需要用@Repeatable註解。Java 8在編譯器層做了優化,相同註解會以集合的方式儲存,因此底層的原理並沒有變化。
擴充套件註解的支援
Java 8擴充套件了註解的上下文,幾乎可以為任何東西添加註解,包括區域性變數、泛型類、父類與介面的實現,連方法的異常也能添加註解。
更好的型別推測機制:Java 8在型別推測方面有了很大的提高,這就使程式碼更整潔,不需要太多的強制型別轉換了。
編譯器優化:Java 8將方法的引數名加入了位元組碼中,這樣在執行時通過反射就能獲取到引數名,只需要在編譯時使用-parameters引數。
並行(parallel)陣列:支援對陣列進行並行處理,主要是parallelSort()方法,它可以在多核機器上極大提高陣列排序的速度。
併發(Concurrency):在新增Stream機制與Lambda的基礎之上,加入了一些新方法來支援聚集操作。
類依賴分析器jdeps:可以顯示Java類的包級別或類級別的依賴。
JVM的PermGen空間被移除:取代它的是Metaspace(JEP 122)。
http://www.cnblogs.com/pkufork/p/java_8.html