java8 新特性
核心內(nèi)容
- Lambda:函數(shù)式接口編程乌询,簡(jiǎn)化代碼量翔试,類(lèi)型上下文自動(dòng)推斷厦幅。
- Stream Api:
1. Lambda 表達(dá)式
概念
java8引入面向函數(shù)編程舌缤,其中面向函數(shù)編程將函數(shù)作為參數(shù)傳遞瓣距,由于java是基于對(duì)象的黔帕,不存在單獨(dú)的函數(shù)。因此java8取巧蹈丸,采用單內(nèi)部函數(shù)的接口作為函數(shù)參數(shù)成黄,實(shí)習(xí)了傳遞函數(shù)的功能呐芥。Lambda表達(dá)式即實(shí)現(xiàn)了以上功能。
語(yǔ)法
lambda表達(dá)式的語(yǔ)法:由參數(shù)列表奋岁、箭頭符號(hào)->和函數(shù)體組成思瘟。如:
// 單句函數(shù)體無(wú)需{}
Runnable r1 = () -> System.out.println("無(wú)參數(shù)的lambda表達(dá)式");
r1.run();
// 單輸入?yún)?shù)可以省略
Consumer<String> c1 = (name) -> System.out.println("我的大名是"+name);
Consumer<String> c2 = name -> System.out.println("我的大名是"+name);
c1.accept("zhangjue");
c2.accept("張玨");
// 單語(yǔ)句return可以省略
Supplier<String> s1 = () -> "張玨教你學(xué)lambda";
System.out.println(s1.get());
// 范性中不支持基本類(lèi)型
//Function< String, int> f1 = name -> return (Integer)18;
Function< String, Integer> f1 = name -> {
return 18;
};
System.out.println("張玨今年"+f1.apply("zhangjue")+"歲");
// {}最后記得加;
Predicate<String> p1 = name -> {
if( "張玨".equals(name))
{
return true;
}
else
{
return false;
}
};
if(p1.test("zy")){
System.out.println("找到了大神");
}
else {
System.out.println("可能是菜雞");
}
函數(shù)式接口
- 只包含一個(gè)抽象方法的接口,稱(chēng)為函數(shù)式接口
- @FunctionalInterface: 檢驗(yàn)是否符號(hào)函數(shù)式接口要求
- java.util.function包:它里面包含了常用的函數(shù)式接口
給定的lambda表達(dá)式闻伶,它的類(lèi)型是由其上下文推導(dǎo)而來(lái)滨攻。這意味著同樣的lambda表達(dá)式在不同上下文里可以擁有不同的類(lèi)型。這也是為什么lambda表達(dá)式中的參數(shù)不用寫(xiě)類(lèi)型的原因蓝翰。
例如:
Callable c =()->"done"; //Callable類(lèi)型
PrivilegedAction a =()->"done"; //PrivilegedAction類(lèi)型
目標(biāo)類(lèi)型 T 需要滿足的條件:
- lambda表達(dá)式的參數(shù)和T的方法參數(shù)在數(shù)量和類(lèi)型上一一對(duì)應(yīng)
- lambda表達(dá)式的返回值和T的方法返回值相兼容
- lambda表達(dá)式內(nèi)所拋出的異常和T的方法throws類(lèi)型相兼容
3. 方法引用與構(gòu)造器引用
使用現(xiàn)成的方法或者構(gòu)造器代替lambda表達(dá)式光绕。具體方式有以下幾種。
方法引用
- 實(shí)例對(duì)象::實(shí)例方法名
- 類(lèi)名::靜態(tài)方法名
- 類(lèi)名::實(shí)例方法名
構(gòu)造器引用
- 類(lèi)名::new
- 類(lèi)型[]::new
代碼:
String str = "zhangjue";
Float f = 3.14f;
// 實(shí)例對(duì)象::實(shí)例方法名畜份,沒(méi)有參數(shù)
//Supplier<String> sup = () -> str.toUpperCase();
Supplier<String> sup = str::toUpperCase;
System.out.println(sup.get());
// 實(shí)例對(duì)象::實(shí)例方法名诞帐,有參數(shù)。
//Consumer con = (x) -> System.out.println(x);
Consumer<String> con = System.out::println; // 不指定參數(shù)
con.accept(str); //自動(dòng)指定參數(shù)
//類(lèi)名::靜態(tài)方法名爆雹。參數(shù)和上面的一樣停蕉。
//Function<Float, Integer> fun = (x) -> Math.round(x);
Function<Float, Integer> fun = Math::round;
System.out.println(fun.apply(f));
//實(shí)例對(duì)象::方法名。參數(shù)和上面的一樣钙态。必須非靜態(tài)方法慧起。
//Consumer<Client> cli = (x) -> x.say();
Consumer<Client> cli = Client::sayHello;
cli.accept(new Client());
// 類(lèi)名::new
//Function<String, StringBuffer> funConstruct = (x) -> new StringBuffer(x);
Function<String, StringBuffer> funConstruct = StringBuffer::new;
// 類(lèi)型[]::new
//Function<Integer, String[]> funString = (e) -> new String[e];
Function<Integer, String[]> funString = String[]::new;
String[] strArray = funString.apply(7);
4. Stream API
位于java.util.stream包:
Stream 是 Java8 提供一種處理數(shù)據(jù)的方式,多個(gè)中間操作可以連接起來(lái)形成一個(gè)流水操作驯绎,直到遇到終止操作時(shí)才一次性全部執(zhí)行完慧,稱(chēng)為“惰性求值”。
Stream API的并發(fā)采用的是我們熟悉的fork/join模式剩失,采用Stream API來(lái)進(jìn)行集合對(duì)象上的并發(fā)操作不需要編寫(xiě)多線程代碼屈尼,也極大的簡(jiǎn)化了編程難度。Stream API 可以聲明性地通過(guò) parallel() 與 sequential() 在并行流與順序流之間進(jìn)行切換拴孤。
特點(diǎn)
- Stream 自己不會(huì)存儲(chǔ)元素脾歧。
- Stream 不會(huì)改變?cè)磳?duì)象。相反演熟,他們會(huì)返回一個(gè)持有結(jié)果的新Stream鞭执。
- Stream 操作是延遲執(zhí)行的。這意味著他們會(huì)等到需要結(jié)果的時(shí)候才執(zhí)行
使用一個(gè)Stream的步驟分為:創(chuàng)建芒粹、中間操作兄纺、終止操作。
創(chuàng)建 Stream
-
Collection 接口被擴(kuò)展化漆,提供了兩個(gè)獲取流的方法:
default Stream<E> stream() : 返回一個(gè)順序流
-
default Stream<E> parallelStream() : 返回一個(gè)并行流
ArrayList<User> users = new ArrayList<>(); Stream<User> stream = users.stream(); Stream<User> stream = users. parallelStream();
-
Arrays 的靜態(tài)方法 stream() 可 以獲取數(shù)組流
重載類(lèi)型
- public static IntStream stream(int[] array)
- public static LongStream stream(long[] array)
- public static DoubleStream stream(double[] array)
-
Stream.of(), 通過(guò)顯示值 創(chuàng)建一個(gè)流估脆。它可以接收任意數(shù)量的參數(shù)。
String[] str = {"Hello World", "Hello Java", "Hello Steam"}; Stream<String> stream = Stream.of(str); Stream<Integer> stream = Stream.of(1, 2, 3, 4);
Stream.iterate() 和 Stream.generate(), 創(chuàng)建無(wú)限流座云。
中間操作
篩選與切片:
映射:
排序:
終止操作(終端操作)
查找與匹配:
歸約:
map 和 reduce 的連接通常稱(chēng)為 map-reduce 模式疙赠,被google所采用付材。
收集:
收集操作的靜態(tài)方法
注:一個(gè)流只能執(zhí)行一個(gè)結(jié)束操作,當(dāng)執(zhí)行了結(jié)束操作以后這個(gè)流就不能再被執(zhí)行圃阳,也就是說(shuō)不能再次進(jìn)行中間操作或結(jié)束操作厌衔,所以結(jié)束操作一定是流的最后一個(gè)操作。
5. 接口中的默認(rèn)方法與靜態(tài)方法
Java 8中允許接口中包含具有具體實(shí)現(xiàn)的方法捍岳,該方法稱(chēng)為 “默認(rèn)方法”富寿,默認(rèn)方法使用 default 關(guān)鍵字修飾。
接口中允許添加靜態(tài)方法祟同。
默認(rèn)方法的”類(lèi)優(yōu)先”原則:
接口中定義了一個(gè)默認(rèn)方法作喘,父類(lèi)定義了一個(gè)同名的方法時(shí)選擇父類(lèi)中的方法理疙。
接口沖突:父接口提供一個(gè)默認(rèn)方法晕城,另一個(gè)接口也提供了一個(gè)具有相同名稱(chēng)和參數(shù)列表的方法(不管方法是否是默認(rèn)方法),那么必須覆蓋該方法來(lái)解決沖突窖贤。
6. Optional 類(lèi)
java.util.Optional包內(nèi)砖顷。是一個(gè)容器類(lèi),代表一個(gè)值存在或不存在赃梧,原來(lái)用 null 表示一個(gè)值不存在滤蝠,現(xiàn)在 Optional 可以避免空指針異常。
常用方法
Optional.of(T t) //創(chuàng)建一個(gè) Optional 實(shí)例
Optional.empty() //創(chuàng)建一個(gè)空的 Optional 實(shí)例
Optional.ofNullable(T t) //若 t 不為 null,創(chuàng)建 Optional 實(shí)例,否則創(chuàng)建空實(shí)例 isPresent() //判斷是否包含值
orElse(T t) //如果調(diào)用對(duì)象包含值授嘀,返回該值物咳,否則返回t
orElseGet(Supplier s) //如果調(diào)用對(duì)象包含值,返回該值蹄皱,否則返回 s 獲取的值
map(Function f) //如果有值對(duì)其處理览闰,并返回處理后的Optional,否則返回 Optional.empty()
flatMap(Function mapper) //與 map 類(lèi)似巷折,要求返回值必須是Optional
7. Fork/Join 框架
將一個(gè)大任務(wù)压鉴,進(jìn)行拆分(fork)成若干個(gè) 小任務(wù)(拆到不可再拆時(shí)),再將一個(gè)個(gè)的小任務(wù)運(yùn)算的結(jié)果進(jìn)行 join 匯總
采用 “工作竊取”模式:
在一般的線程池中,如果一個(gè)線程正在執(zhí)行的任務(wù)由于某些原因 無(wú)法繼續(xù)運(yùn)行,那么該線程會(huì)處于等待狀態(tài).而在fork/join框架實(shí)現(xiàn)中,如果 某個(gè)子問(wèn)題由于等待另外一個(gè)子問(wèn)題的完成而無(wú)法繼續(xù)運(yùn)行.那么處理該子 問(wèn)題的線程會(huì)主動(dòng)尋找其他尚未運(yùn)行的子問(wèn)題來(lái)執(zhí)行.這種方式減少了線程 的等待時(shí)間,提高了性能.