真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Java8有哪些特性

本篇內(nèi)容介紹了“Java8有哪些特性”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)公司!專注于網(wǎng)頁設(shè)計、網(wǎng)站建設(shè)、微信開發(fā)、微信小程序、集團企業(yè)網(wǎng)站建設(shè)等服務(wù)項目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了哈巴河免費建站歡迎大家使用!

Java 8 新特性:

  • Stream API

  • 向方法傳遞代碼的技巧

  • 接口中的默認(rèn)方法

助記:

//Java8主要有哪些新特性?

//1.Stream API 2.接口的默認(rèn)實現(xiàn) 3.方法晉升一級公民
inventory.stream().filter((Apple a) -> a.getWeight() > 150)
			.collect(Collectors.toList());

在Java 8之前:

//對inventory中的蘋果按照重量進行排序
Collections.sort(inventory, new Comparator() {
	public int compare(Apple a1, Apple a2){
		return a1.getWeight().compareTo(a2.getWeight());
	}
});

在Java 8之后:

//給庫存排序,比較蘋果重量,讀起來言簡意賅
inventory.sort(Collectors.comparing(Apple::getWeight));

Java 8提供了一個新的API(稱為“流”,Stream),它支持許多處理數(shù)據(jù)的并行操作,其思路和在數(shù)據(jù)庫查詢語言中的思路類似——用更高級的方式表達(dá)想要的東西,而由“實現(xiàn)”(在這里是Streams庫)來選擇最佳低級執(zhí)行機制。這樣就可以避免用synchronized編寫代碼,這一代碼不僅容易出錯,而且在多核CPU上執(zhí)行所需的成本也比你想象的要高。(更高效利用多核CPU)

從有點修正主義的角度來看,在Java 8中加入Streams可以看作把另外兩項擴充加入Java 8的直接原因:把代碼傳遞給方法的簡潔方式(方法引用、Lambda)和接口中的默認(rèn)方法。

如果僅僅“把代碼傳遞給方法”看作Streams的一個結(jié)果,那就低估了它在Java 8中的應(yīng)用范圍。它提供了一種新的方式,這種方式簡潔地表達(dá)了行為參數(shù)化。比方說,你想要寫兩個只有幾行代碼不同的方法,那現(xiàn)在你只需要把不同的那部分代碼作為參數(shù)傳遞進去就可以了。采用這種編程技巧,代碼會更短、更清晰,也比常用的復(fù)制粘貼更不容易出錯。

Java 8里面將代碼傳遞給方法的功能(同時也能夠返回代碼并將其包含在數(shù)據(jù)結(jié)構(gòu)中)還讓我們能夠使用一整套新技巧,通常稱為函數(shù)式編程。一言以蔽之,這種被函數(shù)式編程界稱為函數(shù)的代碼,可以被來回傳遞并加以組合,以產(chǎn)生強大的編程語匯。

Java 怎么還在變

Java 在編程語言生態(tài)系統(tǒng)中的位置

流處理

流是一系列數(shù)據(jù)項,一次只生成一項。程序可以從輸入流中一個一個讀取數(shù)據(jù)項,然后以同樣的方式將數(shù)據(jù)項寫入輸出流。一個程序的輸出流很可能是另一個程序的輸入流。

舉一個例子:Unix的cat命令會把兩個文件連接起來創(chuàng)建一個流,tr會轉(zhuǎn)換流中的字符,sort會對流中的行進行排序,而tail -3則給出流的最后三行。Unix命令行允許這些程序通過管道(|)連接在一起,命令如下

cat file1 file2 | tr "[A-Z]" "[a-z]" | sort | tail -3

Java8有哪些特性

基于這一思想,Java 8在java.util.stream中添加了一個Stream API;Stream就是一系列T類型的項目。你現(xiàn)在可以把它看成一種比較花哨的迭代器。Stream API的很多方法可以鏈接起來形成一個復(fù)雜的流水線,就像先前例子里面鏈接起來的Unix命令一樣。

推動這種做法的關(guān)鍵在于,現(xiàn)在你可以在一個更高的抽象層次上寫Java 8程序了:思路變成了把這樣的流變成那樣的流(就像寫數(shù)據(jù)庫查詢語句時的那種思路),而不是一次只處理一個項目。另一個好處是,Java 8可以透明地把輸入的不相關(guān)部分拿到幾個CPU內(nèi)核上去分別執(zhí)行你的Stream操作流水線——這是幾乎簡單易行的并行,用不著去費勁搞Thread了

用行為參數(shù)化把代碼傳遞給方法

方法當(dāng)作參數(shù)傳入方法

Java8前只能傳基本類型,對象類型,不能單純存入方法

并行與共享的可變數(shù)據(jù)

一般情況下這就意味著,寫代碼時不能訪問共享的可變數(shù)據(jù)。這些函數(shù)有時被稱為“純函數(shù)”或“無副作用函數(shù)”或“無狀態(tài)函數(shù)”,

并行只有在假定你的代碼的多個副本可以獨立工作時才能進行。但如果要寫入的是一個共享變量或?qū)ο?,這就行不通了:如果兩個進程需要同時修改這個共享變量怎么辦?

Java 8的流實現(xiàn)并行比Java現(xiàn)有的線程API更容易,因此,盡管可以使用synchronized來打破“不能有共享的可變數(shù)據(jù)”這一規(guī)則,但這相當(dāng)于是在和整個體系作對,因為它使所有圍繞這一規(guī)則做出的優(yōu)化都失去意義了。在多個處理器內(nèi)核之間使用synchronized,其代價往往比你預(yù)期的要大得多,因為同步迫使代碼按照順序執(zhí)行,而這與并行處理的宗旨相悖。

這兩個要點(沒有共享的可變數(shù)據(jù),將方法和函數(shù)即代碼傳遞給其他方法的能力)是函數(shù)式編程范式的基石。與此相反,在命令式編程范式中,你寫的程序則是一系列改變狀態(tài)的指令。

“不能有共享的可變數(shù)據(jù)”的要求意味著,一個方法是可以通過它將參數(shù)值轉(zhuǎn)換為結(jié)果的方式完全描述的;換句話說,它的行為就像一個數(shù)學(xué)函數(shù),沒有可見的副作用。

Java 需要演變

你之前已經(jīng)見過了Java的演變。例如,引入泛型,使用List而不只是List,可能一開始都挺煩人的。但現(xiàn)在你已經(jīng)熟悉了這種風(fēng)格和它所帶來的好處,即在編譯時能發(fā)現(xiàn)更多錯誤,且代碼更易讀,因為你現(xiàn)在知道列表里面是什么了。

其他改變讓普通的東西更容易表達(dá),比如,使用for-each循環(huán)而不用暴露Iterator里面的套路寫法。

Java 8中的主要變化反映了它開始遠(yuǎn)離常側(cè)重改變現(xiàn)有值的經(jīng)典面向?qū)ο笏枷?/strong>,而向函數(shù)式編程領(lǐng)域轉(zhuǎn)變,在大面上考慮做什么(例如,創(chuàng)建一個值代表所有從A到B低于給定價格的交通線路)被認(rèn)為是頭等大事,并和如何實現(xiàn)(例如,掃描一個數(shù)據(jù)結(jié)構(gòu)并修改某些元素)區(qū)分開來。

請注意,如果極端點兒來說,傳統(tǒng)的面向?qū)ο缶幊毯秃瘮?shù)式可能看起來是沖突的。但是我們的理念是獲得兩種編程范式中最好的東西,這樣你就有更大的機會為任務(wù)找到理想的工具了。(取長補短)

語言需要不斷改進以跟進硬件的更新或滿足程序員的期待。要堅持下去,Java必須通過增加新功能來改進,而且只有新功能被人使用,變化才有意義。所以,使用Java 8,你就是在保護你作為Java程序員的職業(yè)生涯

Java 中的函數(shù)

編程語言中的函數(shù)一詞通常是指方法,尤其是靜態(tài)方法;這是在數(shù)學(xué)函數(shù),也就是沒有副作用的函數(shù)之外的新含義。

Java 8中新增了函數(shù)——值的一種新形式。有了它,Java 8可以進行多核處理器上的并行編程

想想Java程序可能操作的值吧。首先有原始值,比如42(int類型)和3.14(double類型)。其次,值可以是對象(更嚴(yán)格地說是對象的引用)。獲得對象的唯一途徑是利用new,也許是通過工廠方法或庫函數(shù)實現(xiàn)的;對象引用指向類的一個實例。例子包括"abc"(String類型),new Integer(1111)(Integer類型),以及new HashMap(100)的結(jié)果——它顯然調(diào)用了HashMap的構(gòu)造函數(shù),甚至數(shù)組也是對象。

編程語言的整個目的就在于操作值,要是按照歷史上編程語言的傳統(tǒng),這些值因此被稱為一等值(或一等公民,這個術(shù)語是從20世紀(jì)60年代美國民權(quán)運動中借用來的)

編程語言中的其他結(jié)構(gòu)也許有助于我們表示值的結(jié)構(gòu),但在程序執(zhí)行期間不能傳遞,因而是二等公民(Java中如方法和類等)。

用方法來定義類很不錯,類還可以實例化來產(chǎn)生值,但方法和類本身都不是值。這又有什么關(guān)系呢?

人們發(fā)現(xiàn),在運行時傳遞方法能將方法變成一等公民。這在編程中非常有用,因此Java 8的設(shè)計者把這個功能加入到了Java中。

方法和Lambda 作為一等公民

Scala和Groovy等語言的實踐已經(jīng)證明,讓方法等概念作為一等值可以擴充程序員的工具庫,從而讓編程變得更容易。

Java 8的第一個新功能是方法引用

MethodArgument

Java 8 之前:

File[] hiddenFiles = new File(".").listFiles(new FileFilter() {
	public boolean accept(File file) {
		return file.isHidden();
	}
});

Java 8 之后:

File[] hiddenFiles = new File(".").listFiles(File::isHidden);

Java 8的方法引用::語法(即“把這個方法作為值”)將其傳給listFiles方法,也開始用函數(shù)代表方法了。一個好處是,你的代碼現(xiàn)在讀起來更接近問題的陳述了。方法不再是二等值了。

與用對象引用傳遞對象類似(對象引用是用new創(chuàng)建的),在Java 8里寫下File::isHidden的時候,創(chuàng)建了一個方法引用,同樣可以傳遞它

Java8有哪些特性


Lambda——匿名函數(shù)

除了允許(命名)函數(shù)成為一等值外,Java 8還體現(xiàn)了更廣義的將函數(shù)作為值的思想,包括Lambda(或匿名函數(shù))。比如,你現(xiàn)在可以寫(int x) -> x + 1,表示“調(diào)用時給定參數(shù)x,就返回x + 1值的函數(shù)”。

你可能會想這有什么必要呢?因為你可以在MyMathsUtils類里面定義一個add1方法,然后寫MyMathsUtils::add1嘛!確實是可以,但要是你沒有方便的方法和類可用,新的Lambda語法更簡潔。

傳遞代碼:一個例子

FilteringApples

需求

假設(shè)你有一個Apple類,它有一個getColor方法,還有一個變量inventory保存著一個Apples的列表。你可能想要選出所有的綠蘋果,并返回一個列表。

Java 8之前的寫法:

public static List filterGreenApples(List inventory) {
	List result = new ArrayList<>();
	for (Apple apple : inventory) {
		if ("green".equals(apple.getColor())) {
			result.add(apple);
		}
	}
	return result;
}

另一個新需求

可能想要選出重量超過150克的蘋果

public static List filterHeavyApples(List inventory) {
	List result = new ArrayList<>();
	for (Apple apple : inventory) {
		if (apple.getWeight() > 150) {
			result.add(apple);
		}
	}
	return result;
}

上面有代碼重復(fù),重構(gòu)的氣味出現(xiàn)


Java 8會把條件代碼作為參數(shù)傳遞進去,這樣可以避免filter方法出現(xiàn)重復(fù)的代碼

public static boolean isGreenApple(Apple apple) {
	return "green".equals(apple.getColor());
}

public static boolean isHeavyApple(Apple apple) {
	return apple.getWeight() > 150;
}

public static List filterApples(List inventory, java.util.function.Predicate p) {
	List result = new ArrayList<>();
	for (Apple apple : inventory) {
		if (p.test(apple)) {
			result.add(apple);
		}
	}
	return result;
}

要用它的話,可以寫成

filterApples(inventory, FilteringApples::isGreenApple);

filterApples(inventory, FilteringApples::isHeavyApple);

什么是謂詞Predicate?

在數(shù)學(xué)上常常用來代表一個類似函數(shù)的東西,它接受一個參數(shù)值,并返回true或false

從傳遞方法到Lambda

把方法作為值來傳遞顯然很有用,但要是為類似于isHeavyApple和isGreenApple這種可能只用一兩次的短方法寫一堆定義很煩人。

filterApples(inventory, (Apple a) -> "green".equals(a.getColor()));

//or

filterApples(inventory, (Apple a) -> a.getWeight() > 150 );

//or

filterApples(inventory, (Apple a) -> a.getWeight() < 80
					|| "brown".equals(a.getColor()) );

都不需要為只用一次的方法寫定義;代碼更干凈、更清晰,因為你用不著去找自己到底傳遞了什么代碼。

但要是Lambda的長度多于幾行(它的行為也不是一目了然)的話,那你還是應(yīng)該用方法引用來指向一個有描述性名稱的方法,而不是使用匿名的Lambda。你應(yīng)該以代碼的清晰度為準(zhǔn)繩。

Java 8的設(shè)計師幾乎可以就此打住了,要是沒有多核CPU,可能他們真的就到此為止了。

我們迄今為止談到的函數(shù)式編程竟然如此強大,在后面你更會體會到這一點。本來,Java加上filter和幾個相關(guān)的東西作為通用庫方法就足以讓人滿意了,比如

static  Collection filter(Collection c, Predicate p);

filterApples(inventory, (Apple a) -> a.getWeight() > 150);

就可以直接調(diào)用庫方法filter

inventory.stream().filter((Apple a) -> a.getWeight() > 150)
			.collect(Collectors.toList())

幾乎每個Java應(yīng)用都會制造和處理集合。但集合用起來并不總是那么理想。

需求

Java 8前

比方說,你需要從一個列表中篩選金額較高的交易,然后按貨幣分組。你需要寫一大堆套路化的代碼來實現(xiàn)這個數(shù)據(jù)處理命令。

Map> transactionsByCurrencies = new HashMap<>();

for (Transaction transaction : transactions) {
	if(transaction.getPrice() > 1000){
		Currency currency = transaction.getCurrency();

		List transactionsForCurrency = transactionsByCurrencies.get(currency);

		if (transactionsForCurrency == null) {
			transactionsForCurrency = new ArrayList<>();
			transactionsByCurrencies.put(currency,
				transactionsForCurrency);
		}
		transactionsForCurrency.add(transaction);
	}
}

Java 8后

import static java.util.stream.Collectors.toList;
Map> transactionsByCurrencies = transactions.stream()
		.filter((Transaction t) -> t.getPrice() > 1000)
		.collect(groupingBy(Transaction::getCurrency));

和Collection API相比,Stream API處理數(shù)據(jù)的方式非常不同。用集合的話,你得自己去做迭代的過程。你得用for-each循環(huán)一個個去迭代元素,然后再處理元素。我們把這種數(shù)據(jù)迭代的方法稱為外部迭代。

相反,有了Stream API,根本用不著操心循環(huán)的事情。數(shù)據(jù)處理完全是在庫內(nèi)部進行的。我們把這種思想叫作內(nèi)部迭代。

使用流的好處——更高效利用多核CPU

使用集合的另一個頭疼的地方是,想想看,要是你的交易量非常龐大,你要怎么處理這個巨大的列表呢?單個CPU根本搞不定這么大量的數(shù)據(jù),但你很可能已經(jīng)有了一臺多核電腦。理想的情況下,你可能想讓這些CPU內(nèi)核共同分擔(dān)處理工作,以縮短處理時間。理論上來說,要是你有八個核,那并行起來,處理數(shù)據(jù)的速度應(yīng)該是單核的八倍。

傳統(tǒng)上是利用synchronized關(guān)鍵字,但是要是用錯了地方,就可能出現(xiàn)很多難以察覺的錯誤。Java 8基于Stream的并行提倡很少使用synchronized的函數(shù)式編程風(fēng)格,它關(guān)注數(shù)據(jù)分塊而不是協(xié)調(diào)訪問。

多線程并非易事

問題在于,通過多線程代碼來利用并行(使用先前Java版本中的Thread API)并非易事。

譬如:線程可能會同時訪問并更新共享變量

因此,如果沒有協(xié)調(diào)好,數(shù)據(jù)可能會被意外改變。相比一步步執(zhí)行的順序模型,這個模型不太好理解。

下圖就展示了如果沒有同步好,兩個線程同時向共享變量sum加上一個數(shù)時,可能出現(xiàn)的問題。

Java8有哪些特性

Java 8也用Stream API(java.util.stream)解決了這兩個問題:集合處理時的套路和晦澀,以及難以利用多核。

這樣設(shè)計的第一個原因是,有許多反復(fù)出現(xiàn)的數(shù)據(jù)處理模式,類似于前一節(jié)所說的filterApples或SQL等數(shù)據(jù)庫查詢語言里熟悉的操作,如果在庫中有這些就會很方便:根據(jù)標(biāo)準(zhǔn)篩選數(shù)據(jù)(比如較重的蘋果),提取數(shù)據(jù)(例如抽取列表中每個蘋果的重量字段),或給數(shù)據(jù)分組(例如,將一個數(shù)字列表分組,奇數(shù)和偶數(shù)分別列表)等。

第二個原因是,這類操作常??梢圆⑿谢?/strong>。

例如,如下圖所示,在兩個CPU上篩選列表,可以讓一個CPU處理列表的前一半,第二個CPU處理后一半,這稱為分支步驟(1)。CPU隨后對各自的半個列表做篩選(2)。最后(3),一個CPU會把兩個結(jié)果合并起來

Java8有哪些特性

現(xiàn)在最好記得,Collection主要是為了存儲和訪問數(shù)據(jù),而Stream則主要用于描述對數(shù)據(jù)的計算

// 順序處理
List heavyApples3 = inventory.stream().filter((Apple a) -> a.getWeight() > 150)
		.collect(Collectors.toList());
System.out.println(heavyApples3);

// 并行處理
List heavyApples4 = inventory.parallelStream().filter((Apple a) -> a.getWeight() > 150)
		.collect(Collectors.toList());
System.out.println(heavyApples4);

Java中的并行與無共享可變狀態(tài)

大家都說Java里面并行很難,而且和synchronized相關(guān)的玩意兒都容易出問題。那Java 8里面有什么“靈丹妙藥”呢?事實上有兩個。

首先,庫會負(fù)責(zé)分塊,即把大的流分成幾個小的流,以便并行處理。

其次,流提供的這個幾乎免費的并行,只有在傳遞給filter之類的庫方法的方法不會互動(比方說有可變的共享對象)時才能工作。

但是其實這個限制對于程序員來說挺自然的,舉個例子,我們的Apple::isGreenApple就是這樣。確實,雖然函數(shù)式編程中的函數(shù)的主要意思是“把函數(shù)作為一等值”,不過它也常常隱含著第二層意思,即“執(zhí)行時在元素之間無互動”。

默認(rèn)方法

Java 8中加入默認(rèn)方法主要是為了支持庫設(shè)計師,讓他們能夠?qū)懗龈菀赘倪M的接口。

這一方法很重要,因為你會在接口中遇到越來越多的默認(rèn)方法,但由于真正需要編寫默認(rèn)方法的程序員相對較少,而且它們只是有助于程序改進,而不是用于編寫任何具體的程序


譬如

List heavyApples1 = inventory.stream().filter((Apple a) -> a.getWeight() > 150).collect(Collectors.toList());

List heavyApples2 = inventory.parallelStream().filter((Apple a) -> a.a.getWeight() > 150).collect(Collectors.toList());

這里有個問題:在Java 8之前,List并沒有stream或parallelStream方法,它實現(xiàn)的Collection接口也沒有,因為當(dāng)初還沒有想到這些方法嘛!可沒有這些方法,這些代碼就不能編譯。

換作你自己的接口的話,最簡單的解決方案就是讓Java 8的設(shè)計者把stream方法加入Collection接口,并加入ArrayList類的實現(xiàn)。

可要是這樣做,對用戶來說就是噩夢了。有很多的替代集合框架都用Collection API實現(xiàn)了接口。但給接口加入一個新方法,意味著所有的實體類都必須為其提供一個實現(xiàn)。語言設(shè)計者沒法控制Collections所有現(xiàn)有的實現(xiàn),這下你就進退兩難了:你如何改變已發(fā)布的接口而不破壞已有的實現(xiàn)呢?

Java 8的解決方法就是——接口如今可以包含實現(xiàn)類沒有提供實現(xiàn)的方法簽名 了!那誰來實現(xiàn)它呢?缺失的方法主體隨接口提供了(因此就有了默認(rèn)實現(xiàn)),而不是由實現(xiàn)類提供。

這就給接口設(shè)計者提供了一個擴充接口的方式,而不會破壞現(xiàn)有的代碼。Java 8在接口聲明中使用新的default關(guān)鍵字來表示這一點。

例如,在Java 8里,你現(xiàn)在可以直接對List調(diào)用sort方法。它是用Java 8 List接口中如下所示的默認(rèn)方法實現(xiàn)的,它會調(diào)用Collections.sort靜態(tài)方法:

default void sort(Comparator c) {
	Collections.sort(this, c);
}

這意味著List的任何實體類都不需要顯式實現(xiàn)sort,而在以前的Java版本中,除非提供了sort的實現(xiàn),否則這些實體類在重新編譯時都會失敗。

眾所周知,一個類可以實現(xiàn)多個接口,那么,如果在好幾個接口里有多個默認(rèn)實現(xiàn),是否意味著Java中有了某種形式的多重繼承?Java 8用一些限制來避免出現(xiàn)類似于C++中臭名昭著的菱形繼承問題。

“Java8有哪些特性”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!


當(dāng)前題目:Java8有哪些特性
轉(zhuǎn)載注明:http://weahome.cn/article/jsspco.html

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部