java8新特性简述
Java8發布時間是2014年3月19日,距離今日已經很久了,那么Java8新特性你了解嗎?
java8是Java的一次重大升級,巨大的里程碑式的改進!!
Java語言新特性:
1.與傳統結合 -- Lambda(閉包)表達式和函數式接口以及注解
注意:要能寫成Lambda 其對象類一定支持函數式接口的規范,這個規范是jdk檢驗的,那么什么是函數式接口的規范,原理是什么???
敘說背景和原理:
隨著回調模式和函數式編程風格的日益流行,我們需要在Java中提供一種盡可能輕量級的將代碼封裝為數據(Model code as data)的方法。匿名內部類并不是一個好的選擇,因為:
上面的多數問題均在Java SE 8中得以解決:
- 通過提供更簡潔的語法和局部作用域規則,Java SE 8徹底解決了問題1和問題2
- 通過提供更加靈活而且便于優化的表達式語義,Java SE 8繞開了問題3
- 通過允許編譯器推斷變量的“常量性”(finality),Java SE 8減輕了問題4帶來的困擾
不過,Java SE 8的目標并非解決所有上述問題。因此捕獲可變變量(問題4)和非局部控制流(問題5)并不在Java SE 8的范疇之內。(盡管我們可能會在未來提供對這些特性的支持)
盡管匿名內部類有著種種限制和問題,但是它有一個良好的特性,它和Java類型系統結合的十分緊密:每一個函數對象都對應一個接口類型。之所以說這個特性是良好的,是因為:
- 接口是Java類型系統的一部分
- 接口天然就擁有其運行時表示(Runtime representation)
- 接口可以通過Javadoc注釋來表達一些非正式的協定(contract),例如,通過注釋說明該操作應可交換(commutative)
上面提到的ActionListener接口只有一個方法,大多數回調接口都擁有這個特征:比如Runnable接口和Comparator接口。我們把這些只擁有一個方法的接口稱為函數式接口。(之前它們被稱為SAM類型,即單抽象方法類型(Single Abstract Method))
我們并不需要額外的工作來聲明一個接口是函數式接口:編譯器會根據接口的結構自行判斷(判斷過程并非簡單的對接口方法計數:一個接口可能冗余的定義了一個Object已經提供的方法,比如toString(),或者定義了靜態方法或默認方法,這些都不屬于函數式接口方法的范疇)。不過API作者們可以通過@FunctionalInterface注解來顯式指定一個接口是函數式接口(以避免無意聲明了一個符合函數式標準的接口),加上這個注解之后,編譯器就會驗證該接口是否滿足函數式接口的要求。
實現函數式類型的另一種方式是引入一個全新的結構化函數類型,我們也稱其為“箭頭”類型。例如,一個接收String和Object并返回int的函數類型可以被表示為(String, Object) -> int。我們仔細考慮了這個方式,但出于下面的原因,最終將其否定:
- 它會為Java類型系統引入額外的復雜度,并帶來結構類型(Structural Type)和指名類型(Nominal Type)的混用。(Java幾乎全部使用指名類型)
- 它會導致類庫風格的分歧——一些類庫會繼續使用回調接口,而另一些類庫會使用結構化函數類型
- 它的語法會變得十分笨拙,尤其在包含受檢異常(checked exception)之后
- 每個函數類型很難擁有其運行時表示,這意味著開發者會受到類型擦除(erasure)的困擾和局限。比如說,我們無法對方法m(T->U)和m(X->Y)進行重載(Overload)
- java.lang.Runnable
- java.util.concurrent.Callable
- java.security.PrivilegedAction
- java.util.Comparator
- java.io.FileFilter
- java.nio.file.PathMatcher
- java.lang.reflect.InvocationHandler
- java.beans.PropertyChangeListener
- java.awt.event.ActionListener
- javax.swing.event.ChangeListener
JDK 1.8 新增加的函數接口:
- java.util.function
java.util.function 它包含了很多類,用來支持 Java的 函數式編程,該包中的函數式接口有:
| 1 | BiConsumer<T,U> 代表了一個接受兩個輸入參數的操作,并且不返回任何結果 |
| 2 | BiFunction<T,U,R> 代表了一個接受兩個輸入參數的方法,并且返回一個結果 |
| 3 | BinaryOperator<T> 代表了一個作用于于兩個同類型操作符的操作,并且返回了操作符同類型的結果 |
| 4 | BiPredicate<T,U> 代表了一個兩個參數的boolean值方法 |
| 5 | BooleanSupplier 代表了boolean值結果的提供方 |
| 6 | Consumer<T> 代表了接受一個輸入參數并且無返回的操作 |
| 7 | DoubleBinaryOperator 代表了作用于兩個double值操作符的操作,并且返回了一個double值的結果。 |
| 8 | DoubleConsumer 代表一個接受double值參數的操作,并且不返回結果。 |
| 9 | DoubleFunction<R> 代表接受一個double值參數的方法,并且返回結果 |
| 10 | DoublePredicate 代表一個擁有double值參數的boolean值方法 |
| 11 | DoubleSupplier 代表一個double值結構的提供方 |
| 12 | DoubleToIntFunction 接受一個double類型輸入,返回一個int類型結果。 |
| 13 | DoubleToLongFunction 接受一個double類型輸入,返回一個long類型結果 |
| 14 | DoubleUnaryOperator 接受一個參數同為類型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。 |
Lambda表達式例子:
Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) );
在上面這個代碼中的參數e的類型是由編譯器推理得出的,你也可以顯式指定該參數的類型,例如:
Arrays.asList( "a", "b", "d" ).forEach( ( String e ) -> System.out.println( e ) );
如果Lambda表達式需要更復雜的語句塊,則可以使用花括號將該語句塊括起來,類似于Java中的函數體,例如:
Arrays.asList("a","b","d" ).sort(( e1, e2 ) -> e1.compareTo( e2 ) );和
Arrays.asList("a","b","d" ).sort(( e1, e2 ) -> {
int result = e1.compareTo( e2 );return result; } );上述的forEach(),sort() 就是一個Consumer<String>函數式接口,傳入String值。以上是函數式接口的lambda表達,接著看幾個依據函數式接口的方法引用:
public class Car {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實例如下:
finalCarcar =Car.create(Car::new);finalList<Car >cars =Arrays.asList(car); -
靜態方法引用:它的語法是Class::static_method,實例如下:
cars.forEach(Car::collide); -
特定類的任意對象的方法引用:它的語法是Class::method實例如下:
cars.forEach(Car::repair); -
特定對象的方法引用:它的語法是instance::method實例如下:
finalCarpolice =Car.create(Car::new);cars.forEach(police::follow);
Java思想:
Java8 新增加上述的函數式接口是為了干什么?如果你在未實際開發中使用這些接口,而是在了解這些函數式接口就能體會到,那么你對Java的思想的理解還是挺到位的!
上述函數式接口無非就是傳入,提供的操作(返回值不是強制的),注意:因為既然是函數式接口,那么必然是接口實現的過程,只是語法表達上使用函數式罷了。既然接口要么就是回調開始端傳入參數或者對象,也就是“傳入”。要么就是接口回調執行端執行操作之后返回對象(就是提供對象)或者返回值。
那么是不是有人問:傳入值和返回值(對象),直接在函數式表達中聲明不就可以了,其實這樣也可以,只是iava8多了一層對傳入和返回的封裝(對應做成了函數式接口),就是上面羅列的函數式接口。
@FunctionInterface 注解聲明函數式接口,就是聲明一個函數式接口,里面的抽象方法,就是可以使用Lambda表達式;但是注意函數參數類型。
2.接口的默認方法,靜態方法
Java8中允許接口存在靜態方法,增加了默認方法。默認方法就是可以在接口中有自己的函數體(注意不是虛方法),其他擴展接口,那么直接覆蓋默認方法。
3.處理時間、日期的API
4.base64編碼
5.nashorn js 引擎
提到nashorn,必須提到jjs(nashorn命令行工具),在cmd中你為什么配置環境變量,就可以輸入命令行,編譯Java。為什么呢,因為jdk中包含命令行工具,那么Jdk8中,包含了nashorn命令行工具--jjs。
在配置Jdk8環境變量之后,直接在cmd中輸入jjs.
執行js腳本:
創建并保存sample.js在?C:> JAVA?文件夾。
sample.js print('Hello World!');打開控制臺并使用下面的命令。
C:\JAVA>jjs sample.js看到結果
Hello World!在Java中調用js:
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 = "Mahesh"; Integer result = null;try {nashorn.eval("print('" + name + "')");result = (Integer) nashorn.eval("10 + 2"); }catch(ScriptException e){System.out.println("Error executing script: "+ e.getMessage());}System.out.println(result.toString());} }6.并發與并行數組
7.util下面的stream接口
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?
Stream(流)是一個來自數據源的元素隊列并支持聚合操作
- <strong元素隊列< strong="">元素是特定類型的對象,形成一個隊列。 Java中的Stream并不會存儲元素,而是按需計算。
- 數據源?流的來源。 可以是集合,數組,I/O channel, 產生器generator 等。
- 聚合操作?類似SQL語句一樣的操作, 比如filter, map, reduce, find, match, sorted等。
和以前的Collection操作不同, Stream操作還有兩個基礎的特征:
- Pipelining: 中間操作都會返回流對象本身。 這樣多個操作可以串聯成一個管道, 如同流式風格(fluent style)。 這樣做可以對操作進行優化, 比如延遲執行(laziness)和短路( short-circuiting)。
- 內部迭代: 以前對集合遍歷都是通過Iterator或者For-Each的方式, 顯式的在集合外部進行迭代, 這叫做外部迭代。 Stream提供了內部迭代的方式, 通過訪問者模式(Visitor)實現。
生成流
在 Java 8 中, 集合接口有兩個方法來生成流:
-
stream()?? 為集合創建串行流。
-
parallelStream()?? 為集合創建并行流。
forEach
Stream 提供了新的方法 'forEach' 來迭代流中的每個數據。以下代碼片段使用 forEach 輸出了10個隨機數:
Randomrandom =newRandom();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");// 獲取空字符串的數量intcount =strings.stream().filter(string -> string.isEmpty()).count();limit
limit 方法用于獲取指定數量的流。 以下代碼片段使用 limit 方法打印出 10 條數據:
Randomrandom =newRandom();random.ints().limit(10).forEach(System.out::println);sorted
sorted 方法用于對流進行排序。以下代碼片段使用 sorted 方法對輸出的 10 個隨機數進行排序:
Randomrandom =newRandom();random.ints().limit(10).sorted().forEach(System.out::println);并行(parallel)程序
parallelStream 是流并行處理程序的代替方法。以下實例我們使用 parallelStream 來輸出空字符串的數量:
List<String>strings =Arrays.asList("abc","","bc","efg","abcd","","jkl");// 獲取空字符串的數量intcount =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);StringmergedString =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);IntSummaryStatisticsstats =integers.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());總結
以上是生活随笔為你收集整理的java8新特性简述的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 精灵宝可梦letsgo超梦在哪抓(精灵长
- 下一篇: android 限制edittext 最