官方文檔:javascript
https://www.oracle.com/technetwork/cn/java/javase/8-whats-new-2157071-zhs.htmlhtml
https://blog.csdn.net/zwlove5280/article/details/77576216java
http://www.cnblogs.com/onetwo/p/8526374.htmlgit
其實Lambda表達式的本質只是一個"語法糖",由編譯器推斷並幫你轉換包裝爲常規的代碼,所以你可使用更少的代碼來實現一樣的功能程序員
lambda表達式容許你經過表達式來代替功能接口。 lambda表達式就和方法同樣,它提供了一個正常的參數列表和一個使用這些參數的主體(body,能夠是一個表達式或一個代碼塊)github
Lambda表達式的語法
基本語法:
(parameters) -> expression
或
(parameters) ->{ statements; }express
舉個例子apache
// 1. 不須要參數,返回值爲 5 () -> 5 // 2. 接收一個參數(數字類型),返回其2倍的值 x -> 2 * x // 3. 接受2個參數(數字),並返回他們的差值 (x, y) -> x – y // 4. 接收2個int型整數,返回他們的和 (int x, int y) -> x + y // 5. 接受一個 string 對象,並在控制檯打印,不返回任何值(看起來像是返回void) (String s) -> System.out.print(s)
函數式接口是隻有一個方法的接口,用做lambda表達式的類型。可使用註解 @FunctionalInterface 說明這個接口是一個函數式接口編程
舉個例子設計模式
@FunctionalInterface public interface Runnable { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run(); }
new Thread(() -> System.out.println("java8 start !")).start();
舉個例子
懶惰求值:
int x=2; Supplier<Integer> supplier = ()->x+3; int result=supplier.get();
Java 8使用兩個新概念擴展了接口的含義:默認方法和靜態方法。默認方法使得接口有點相似traits,不過要實現的目標不同。默認方法使得開發者能夠在 不破壞二進制兼容性的前提下,往現存接口中添加新的方法,即不強制那些實現了該接口的類也同時實現這個新加的方法。
默認方法和抽象方法之間的區別在於抽象方法須要實現,而默認方法不須要。接口提供的默認方法會被接口的實現類繼承或者覆寫。
舉個例子:
private interface Defaulable { // Interfaces now allow default methods, the implementer may or // may not implement (override) them. default String notRequired() { return "Default implementation"; } } private static class DefaultableImpl implements Defaulable { } private static class OverridableImpl implements Defaulable { @Override public String notRequired() { return "Overridden implementation"; } }
Java 8帶來的另外一個有趣的特性是在接口中能夠定義靜態方法
舉個例子:
private interface DefaulableFactory { // Interfaces now allow static methods static Defaulable create( Supplier< Defaulable > supplier ) { return supplier.get(); } }
因爲JVM上的默認方法的實如今字節碼層面提供了支持,所以效率很是高。默認方法容許在不打破現有繼承體系的基礎上改進接口。該特性在官方庫中的應用是:給java.util.Collection接口添加新方法,如stream()、parallelStream()、forEach()和removeIf()等等。儘管默認方法有這麼多好處,但在實際開發中應該謹慎使用:在複雜的繼承體系中,默認方法可能引發歧義和編譯錯誤。
方法引用是用來直接訪問類或者實例的已經存在的方法或者構造方法。方法引用提供了一種引用而不執行方法的方式,它須要由兼容的函數式接口構成的目標類型上下文。計算時,方法引用會建立函數式接口的一個實例。
當Lambda表達式中只是執行一個方法調用時,不用Lambda表達式,直接經過方法引用的形式可讀性更高一些。方法引用是一種更簡潔易懂的Lambda表達式。
注意方法引用是一個Lambda表達式,其中方法引用的操做符是雙冒號"::"。
舉個例子:
Arrays.sort(stringsArray,(s1,s2)->s1.compareToIgnoreCase(s2));
方法引用:
Arrays.sort(stringsArray, String::compareToIgnoreCase);
自從Java 5中引入註解以來,這個特性開始變得很是流行,並在各個框架和項目中被普遍使用。不過,註解有一個很大的限制是:在同一個地方不能屢次使用同一個註解。Java 8打破了這個限制,引入了重複註解的概念,容許在同一個地方屢次使用同一個註解。
在Java 8中使用@Repeatable註解定義重複註解,實際上,這並非語言層面的改進,而是編譯器作的一個trick,底層的技術仍然相同。
舉個例子:
public class RepeatingAnnotations { @Target( ElementType.TYPE ) @Retention( RetentionPolicy.RUNTIME ) public @interface Filters { Filter[] value(); } @Target( ElementType.TYPE ) @Retention( RetentionPolicy.RUNTIME ) @Repeatable( Filters.class ) public @interface Filter { String value(); }; @Filter( "filter1" ) @Filter( "filter2" ) public interface Filterable { } public static void main(String[] args) { for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) { System.out.println( filter.value() ); } } }
另外,反射API提供了一個新的方法:getAnnotationsByType(),能夠返回某個類型的重複註解,例如Filterable.class.getAnnoation(Filters.class)
將返回兩個Filter實例,輸出到控制檯的內容以下所示:
filter1
filter2
Java 8編譯器在類型推斷方面有很大的提高,在不少場景下編譯器能夠推導出某個參數的數據類型,從而使得代碼更爲簡潔。
舉個例子:
public class Value< T > { public static< T > T defaultValue() { return null; } public T getOrDefault( T value, T defaultValue ) { return ( value != null ) ? value : defaultValue; } }
public class TypeInference { public static void main(String[] args) { final Value< String > value = new Value<>(); value.getOrDefault( "22", Value.defaultValue() ); } }
參數Value.defaultValue()的類型由編譯器推導得出,不須要顯式指明。在Java 7中這段代碼會有編譯錯誤,除非使用Value.<String>defaultValue()
。
Java 8拓寬了註解的應用場景。如今,註解幾乎可使用在任何元素上:局部變量、接口類型、超類和接口實現類,甚至能夠用在函數的異常定義上。
ElementType.TYPE_USER和ElementType.TYPE_PARAMETER是Java 8新增的兩個註解,用於描述註解的使用場景。Java 語言也作了對應的改變,以識別這些新增的註解。
爲了在運行時得到Java程序中方法的參數名稱,老一輩的Java程序員必須使用不一樣方法,例如Paranamer liberary。Java 8終於將這個特性規範化,在語言層面(使用反射API和Parameter.getName()方法)和字節碼層面(使用新的javac編譯器以及-parameters參數)提供支持。
public class ParameterNames { public static void main(String[] args) throws Exception { Method method = ParameterNames.class.getMethod( "main", String[].class ); for( final Parameter parameter: method.getParameters() ) { System.out.println( "Parameter: " + parameter.getName() ); } } }
在Java 8中這個特性是默認關閉的,所以若是不帶-parameters參數編譯上述代碼並運行,則會輸出以下結果:
Parameter: arg0
若是帶-parameters參數,則會輸出以下結果(正確的結果)
Parameter: args
若是你使用Maven進行項目管理,則能夠在maven-compiler-plugin編譯器的配置項中配置-parameters參數:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <compilerArgument>-parameters</compilerArgument> <source>1.8</source> <target>1.8</target> </configuration> </plugin>
Java應用中最多見的bug就是空值異常。在Java 8以前,Google Guava引入了Optionals類來解決NullPointerException,從而避免源碼被各類null檢查污染,以便開發者寫出更加整潔的代碼。Java 8也將Optional加入了官方庫。
Optional僅僅是一個容易:存放T類型的值或者null。它提供了一些有用的接口來避免顯式的null檢查,能夠參考Java 8官方文檔瞭解更多細節。
streams
Java 8 中的 Stream 是對集合(Collection)對象功能的加強,它專一於對集合對象進行各類很是便利、高效的聚合操做(aggregate operation),或者大批量數據操做 (bulk data operation)。Stream API 藉助於一樣新出現的 Lambda 表達式,極大的提升編程效率和程序可讀性。同時它提供串行和並行兩種模式進行匯聚操做,併發模式可以充分利用多核處理器的優點,使用 fork/join 並行方式來拆分任務和加速處理過程。一般編寫並行代碼很難並且容易出錯, 但使用 Stream API 無需編寫一行多線程的代碼,就能夠很方便地寫出高性能的併發程序。因此說,Java 8 中首次出現的 java.util.stream 是一個函數式語言+多核時代綜合影響的產物。
Stream操做是管道操做,操做分爲中間操做和結束操做兩種。整個管道操做的流程爲數據源->中間操做->結束操做。
中間操做
好比:Stream.filter、Stream.map,能夠有0個或者多箇中間操做。中間操做返回的仍然是stream,這些操做常常是「懶惰的」。好比filter操做,並不是真的對源數據進行過濾,而是從新建立一個Stream。並且當多箇中間操做時,不是真的每次都去遍歷源數據,而是在結束操做時,纔會去遍歷源數據,從而執行這些中間操做。在遍歷中,操做的懶惰性表如今,並非每次都是遍歷全部的數據,好比「找出長度大於1000的第一個String」,找到以後就會返回。
中間操做又分爲無狀態和有狀態兩種。好比map和filter是無狀態操做,也就是各元素間的操做是無關的,可單獨進行。而distinct和sorted 是有狀態操做,在操做時須要用到前元素,也就是元素之間的處理是相關的。
有狀態的操做必須所有執行完畢的時候才能裝入結果中。
結束操做
好比Sream.foreach、Stream.reduce,Stream.collect,結束操做是必須的。結束操做的目的是將Stream轉換爲想要的結果或者結構。當結束操做以後,當前管道結束,不能夠再重複使用。若是你仍然想操做源數據,那你必須建立一個新的Stream.
Stream 是惰性的(lazy),就如同 RxJava 中的 lazy Observable 同樣。
Stream<Integer> integerStream = Stream.of(1, 2, 3); Stream<Integer> filterStream = integerStream.filter(integer -> { Log.d(TAG, "integer = " + integer); return integer > 1; });
若是運行這段代碼,不會有任何 Log 出現,那麼 Stream.filter() 有什麼做用呢? Stream.filter() 就如同設計模式中的裝飾模式同樣,它只是對 Stream 進行裝飾,這裏的裝飾指的就是過濾。
像 Steam.filter() 把一個 Stream 轉化爲另一個 Stream 的方法,我稱之爲 過渡操做符(intermediate operations)。
而若是想要 Stream 運做起來,須要 Steam 中的 終止操做符( terminal operation ),例如 Stream.count();
Stream<Integer> integerStream = Stream.of(1, 2, 3); Stream<Integer> filterStream = integerStream.filter(integer -> { Log.d(TAG, "integer = " + integer); return integer > 1; }); long count = filterStream.count();
運行這段代碼就能夠看到 Log 的出現
Stream 只能在操做符( Stream 的方法)上執行一次,例如
Stream<Integer> integerStream = Stream.of(1, 2, 3); integerStream.map(integer -> "s"); integerStream.filter(integer -> integer > 1);
雖然 map 和 filter 都是過渡操做符,可是運行程序後,會報錯
java.lang.IllegalStateException: stream has already been operated upon or closed
因此一個流只能執行一次操做
Stream 如同 RxJava 的 Observable 同樣,發射的元素都是有序的
List<Integer> collect = Stream.of(1, 2, 3, 4, 5) .map(integer -> { Log.d(TAG, "step1: " + integer); return integer; }) .map(integer -> { Log.d(TAG, "step2: " + integer); return integer; }) .collect(Collectors.toList());
由於有 及早求值方法 Stream.collect(),因此能夠看到 Log 信息
D/david: step1: 1 D/david: step2: 1 D/david: step1: 2 D/david: step2: 2 D/david: step1: 3 D/david: step2: 3 D/david: step1: 4 D/david: step2: 4 D/david: step1: 5 D/david: step2: 5
Stream 有一個 close() 方法,而且實現了 AutoCloaseable 接口。可是幾乎全部的 Stream 都不須要在使用後關閉。通常來講,只有 Stream 來源於 IO 管道的,須要手動關閉。
順序執行和並行執行
Stream 能夠順序執行,也能夠並行執行。
Stream 也能夠由建立的方式來決定順序執行仍是並行執行。例如,list.stream() 建立一個順序執行的 Stream,而 list.parallelStream() 建立一個並行執行的 Stream。 然而執行的方式是能夠經過 Stream.parallel() 和 Stream.sequential() 來改變的。
既然能夠改變流的執行方法,天然也能夠查詢流的執行方式,Stream.isParallel() 方法查詢是否並行的流。
public static<T> Stream<T> of(T... values) { return Arrays.stream(values); }
Stream.of() 以類型爲 T 的可變長數組 values 做爲參數,建立 Stream 對象,例如
Stream<Integer> integerStream = Stream.of(1, 2, 3);
Stream.generate(Supplier s) 生成一個無線的流
Stream<Integer> integerStream = Stream.generate(new Supplier<Integer>() { int i = 0; @Override public Integer get() { return i++; } });
若是咱們用一個 Stream 的 終止操做符 就能夠看到無限個元素,例如用 Stream.forEach()
Stream.generate(new Supplier<Integer>() { int i = 0; @Override public Integer get() { return i++; } }).forEach(integer -> Log.d(TAG, "integer = " + integer));
運行後,就能夠看到茫茫多的 Log 信息。
在實際中可能只須要這個無限流中的前10,那麼能夠用 Stream.limite(10) 來限制流中元素的個數
Stream.generate(new Supplier<Integer>() { int i = 0; @Override public Integer get() { return i++; } }).limit(10).forEach(integer -> Log.d(TAG, "integer = " + integer));
再運行代碼,就只能看到前10個元素了。
Stream.iterate() 方法用代碼比較好解釋,這個方法生成的流的關鍵代碼是 Iterator 中的 next() 方法
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) { // ... final Iterator<T> iterator = new Iterator<T>() { @SuppressWarnings("unchecked") T t = (T) Streams.NONE; @Override public boolean hasNext() { return true; } @Override public T next() { // 生成 Stream 中的元素 return t = (t == Streams.NONE) ? seed : f.apply(t); } }; // 用 iterator 建立 Stream // ... }
Stream.iterate() 產生的 Stream 的第一個元素是 seed, 後續的元素是 f(seed), f(f(seed)), f(f(f(seed))) … , 例如
Stream.iterate(1, integer -> ++integer) .limit(3) .forEach(integer -> Log.d(TAG, "integer = " + integer));
產生的結果就是1,2,3 ,也就是對應的 1 , f(1), f(f(1))。
以 Builder 模式建立 Stream
Stream.Builder<String> builder = Stream.builder(); builder.add("hello"); builder.accept("world"); Stream<String> stream = builder.build(); stream.forEach(s -> Log.d(TAG, "s = " + s));
add() 和 accept() 從實現上是等效的,只不過 add() 是 default 方法
Stream.concat(Stream< ? extends T > a, Stream< ? extends T > b),把 Stream b 鏈接到 Stream a 以後,a 和 b 中元素的順序不變
建立一個空的 Stream,目前我還不知道這個到底怎麼用。
List<Integer> list = Arrays.asList(1, 2, 3); Stream<Integer> stream = list.stream();
collection接口的定義以下
/** * @author Josh Bloch * @author Neal Gafter * @see Set * @see List * @see Map * @see SortedSet * @see SortedMap * @see HashSet * @see TreeSet * @see ArrayList * @see LinkedList * @see Vector * @see Collections * @see Arrays * @see AbstractCollection * @since 1.2 */ public interface Collection<E> extends Iterable<E> { default Stream<E> stream() { return StreamSupport.stream(spliterator(), false); } }
int[] ints = new int[]{1, 3, 4}; IntStream stream1 = Arrays.stream(ints);
IntStream
IntStream
BufferedReader
的行組成的流BitSet
中的設置位的索引組成的 IntStream
String
中的字符對應的 IntStream
結束流操做
forEach(Consumer<T> action) 將提供的操做應用於流的每一個元素
ImmutableList.of("aa", "abb","abc","bb","cc").stream() .forEach(s->System.out.println(s));
toArray() 使用流的元素建立一個數組
Object[] array=ImmutableList.of("aa", "abb","abc","bb","cc").stream() .toArray();
reduce(...) 將流的元素聚合爲一個彙總值
String res= ImmutableList.of("a", "b","c","d","e").stream() .reduce("",(result,item)->result+item); //abcde
collect(...) 將流的元素聚合到一個彙總結果容器中
List res= ImmutableList.of("a", "b","c","d","e").stream() .collect(Collectors.toList());
min(Comparator<T>) 經過比較符返回流的最小元素
Optional res = ImmutableList.of("a", "b", "c", "d", "e").stream() .min((s1, s2) -> s1.compareTo(s2)); System.out.println(res.get()); //a
max(Comparator<T>) 經過比較符返回流的最大元素
Optional res = ImmutableList.of("a", "b", "c", "d", "e").stream() .max((s1, s2) -> s1.compareTo(s2)); System.out.println(res.get()); //e
count() 返回流的大小
long res = ImmutableList.of("a", "b", "c", "d", "e").stream() .count();
allMatch(Predicate<T>) 是否是Stream中的全部元素都知足給定的匹配條件
boolean res = ImmutableList.of("a", "b", "c", "d", "e").stream() .allMatch(s->s.contains("a")); // false
anyMatch(Predicate<T>) Stream中是否存在任何一個元素知足匹配條件
boolean res = ImmutableList.of("a", "b", "c", "d", "e").stream() .anyMatch(s->s.contains("a")); //true
noneMatch(Predicate<T>) 是否是Stream中的全部元素都不知足給定的匹配條件
boolean res = ImmutableList.of("a", "b", "c", "d", "e").stream() .noneMatch(s->s.contains("a")); //false
findFirst 返回Stream中的第一個元素,若是Stream爲空,返回空Optional
Optional res = ImmutableList.of("a", "b", "c", "d", "e").stream() .findFirst();
中間流操做
distinct() 去重
long res = ImmutableList.of("a", "b", "a", "d", "e").stream() .distinct().count(); //4
filter(Predicate<T>) 與預期匹配的流的元素
long res = ImmutableList.of("aa", "ab", "abc", "d", "be").stream() .filter(s->s.contains("a")).count(); //3
map(Function<T, U>) 將提供的函數應用於流的元素的結果
List res = ImmutableList.of("aa", "ab", "abc", "d", "be").stream() .map(s->s.toUpperCase()).collect(Collectors.toList());//[AA, AB, ABC, D, BE]
flatMap(Function<T, Stream<U>> 將提供的流處理函數應用於流元素後得到的流元素
List<String> res = ImmutableList.of("hello,world").stream() .flatMap(s -> Arrays.stream(s.split(","))).collect(Collectors.toList());//["hello","world"]
map與flatMap的區別
List<String> words = new ArrayList<String>(); words.add("your"); words.add("name"); public static Stream<Character> characterStream(String s){ List<Character> result = new ArrayList<>(); for (char c : s.toCharArray()) result.add(c); return result.stream(); } Stream<Stream<Character>> result = words.map(w -> characterStream(w)); Stream<Character> letters = words.flatMap(w -> characterStream(w));
sorted 排序
List<String> res = ImmutableList.of("he", "aa", "cc").stream() .sorted().collect(Collectors.toList());
limit(long) 截斷至所提供長度的流元素
List<String> res = ImmutableList.of("he", "aa", "cc").stream() .limit(2).collect(Collectors.toList());
skip(long) 丟棄了前 N 個元素的流元素
List<String> res = ImmutableList.of("he", "aa", "cc").stream() .skip(1).collect(Collectors.toList());
並行流就是把一個內容分紅多個數據塊,並用不一樣的線程分紅多個數據塊,並用不一樣的線程分別處理每一個數據塊的流。
JAVA8 中將並行進行了優化,咱們能夠很容易的對數據進行並行操做。Stream API 能夠聲明性地經過parallel() 與sequential() 在並行流與順序流之間進行切換。其實JAVA8底層是使用JAVA7新加入的Fork/Join框架:
Fork/Join框架與傳統線程池的區別:
採用「工做竊取」模式(work-stealing):當執行新的任務時它能夠將其拆分分紅更小的任務執行,並將小任務加到線程隊列中,而後再從一個隨機線程的隊列中偷一個並把它放在本身的隊列中。相對於通常的線程池實現,fork/join框架的優點體如今對其中包含的任務的處理方式上.在通常的線程池中,若是一個線程正在執行的任務因爲某些緣由沒法繼續運行,那麼該線程會處於等待狀態.而在fork/join框架實現中,若是某個子問題因爲等待另一個子問題的完成而沒法繼續運行.那麼處理該子問題的線程會主動尋找其餘還沒有運行的子問題來執行.這種方式減小了線程的等待時間,提升了性能.
Java 8引入了新的Date-Time API(JSR 310)來改進時間、日期的處理。時間和日期的管理一直是最令Java開發者痛苦的問題。java.util.Date和後來的java.util.Calendar一直沒有解決這個問題(甚至令開發者更加迷茫)。
由於上面這些緣由,誕生了第三方庫Joda-Time,能夠替代Java的時間管理API。Java 8中新的時間和日期管理API深受Joda-Time影響,並吸取了不少Joda-Time的精華。新的java.time包包含了全部關於日期、時間、時區、Instant(跟日期相似可是精確到納秒)、duration(持續時間)和時鐘操做的類。新設計的API認真考慮了這些類的不變性(從java.util.Calendar吸收的教訓),若是某個實例須要修改,則返回一個新的對象。
Java 8提供了新的Nashorn JavaScript引擎,使得咱們能夠在JVM上開發和運行JS應用。Nashorn JavaScript引擎是javax.script.ScriptEngine的另外一個實現版本,這類Script引擎遵循相同的規則,容許Java和JavaScript交互使用
舉個例子:
ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName( "JavaScript" ); System.out.println( engine.getClass().getName() ); System.out.println( "Result:" + engine.eval( "function f() { return 1; }; f() + 1;" ) );
輸出結果:
Result: 2
新的Base64API也支持URL和MINE的編碼解碼。
(Base64.getUrlEncoder() / Base64.getUrlDecoder(), Base64.getMimeEncoder() / Base64.getMimeDecoder())。
BASE64不是用來加密的,是BASE64編碼後的字符串,所有都是由標準鍵盤上面的常規字符組成,這樣編碼後的字符串在網關之間傳遞不會產生UNICODE字符串不能識別或者丟失的現象。你再仔細研究下EMAIL就會發現其實EMAIL就是用base64編碼事後再發送的。而後接收的時候再還原。
Java8版本新增了不少新的方法,用於支持並行數組處理。最重要的方法是parallelSort(),能夠顯著加快多核機器上的數組排序
舉個例子:
long[] arrayOfLong = new long [ 20000 ]; Arrays.parallelSetAll( arrayOfLong, index -> ThreadLocalRandom.current().nextInt( 1000000 ) ); Arrays.stream( arrayOfLong ).limit( 10 ).forEach( i -> System.out.print( i + " " ) ); System.out.println();
基於新增的lambda表達式和steam特性,爲Java 8中爲java.util.concurrent.ConcurrentHashMap類添加了新的方法來支持聚焦操做;另外,也爲java.util.concurrentForkJoinPool類添加了新的方法來支持通用線程池操做(更多內容能夠參考咱們的併發編程課程)。
Java 8還添加了新的java.util.concurrent.locks.StampedLock類,用於支持基於容量的鎖——該鎖有三個模型用於支持讀寫操做(能夠把這個鎖當作是java.util.concurrent.locks.ReadWriteLock的替代者)。
在java.util.concurrent.atomic包中也新增了很多工具類,列舉以下:
使用Metaspace(JEP 122)代替持久代(PermGen space)。在JVM參數方面,使用-XX:MetaSpaceSize和-XX:MaxMetaspaceSize代替原來的-XX:PermSize和-XX:MaxPermSize。