1. 程式人生 > >java 8的新特性(全)

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());
    }
}

針對上述程式碼例子,我們來學習一下方法引用的幾種形式:

  1. 構造器引用:它的語法是Class::new,或者更一般的Class< T >::new例項如下:
  2. 靜態方法引用以及特定類的任意物件的方法引用:它的語法是Class::static_method\Class::method,例項如下:
cars.forEach( Car::collide );//static method
cars.forEach( Car::repair );//method
  1. 特定物件的方法引用:它的語法是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