溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Java8有哪些特性

發(fā)布時(shí)間:2021-11-17 13:43:31 來(lái)源:億速云 閱讀:121 作者:iii 欄目:大數(shù)據(jù)

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

Java 8 新特性:

  • Stream API

  • 向方法傳遞代碼的技巧

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

助記:

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

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

在Java 8之前:

//對(duì)inventory中的蘋(píng)果按照重量進(jìn)行排序
Collections.sort(inventory, new Comparator<Apple>() {
	public int compare(Apple a1, Apple a2){
		return a1.getWeight().compareTo(a2.getWeight());
	}
});

在Java 8之后:

//給庫(kù)存排序,比較蘋(píng)果重量,讀起來(lái)言簡(jiǎn)意賅
inventory.sort(Collectors.comparing(Apple::getWeight));

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

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

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

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

Java 怎么還在變

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

流處理

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

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

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

Java8有哪些特性

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

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

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

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

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

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

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

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

Java 8的流實(shí)現(xiàn)并行比Java現(xiàn)有的線程API更容易,因此,盡管可以使用synchronized來(lái)打破“不能有共享的可變數(shù)據(jù)”這一規(guī)則,但這相當(dāng)于是在和整個(gè)體系作對(duì),因?yàn)樗顾袊@這一規(guī)則做出的優(yōu)化都失去意義了。在多個(gè)處理器內(nèi)核之間使用synchronized,其代價(jià)往往比你預(yù)期的要大得多,因?yàn)橥狡仁勾a按照順序執(zhí)行,而這與并行處理的宗旨相悖。

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

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

Java 需要演變

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

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

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

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

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

Java 中的函數(shù)

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

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

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

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

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

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

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

方法和Lambda 作為一等公民

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

Java 8的第一個(gè)新功能是方法引用

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的方法引用::語(yǔ)法(即“把這個(gè)方法作為值”)將其傳給listFiles方法,也開(kāi)始用函數(shù)代表方法了。一個(gè)好處是,你的代碼現(xiàn)在讀起來(lái)更接近問(wèn)題的陳述了。方法不再是二等值了。

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

Java8有哪些特性


Lambda——匿名函數(shù)

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

你可能會(huì)想這有什么必要呢?因?yàn)槟憧梢栽贛yMathsUtils類里面定義一個(gè)add1方法,然后寫(xiě)MyMathsUtils::add1嘛!確實(shí)是可以,但要是你沒(méi)有方便的方法和類可用,新的Lambda語(yǔ)法更簡(jiǎn)潔。

傳遞代碼:一個(gè)例子

FilteringApples

需求

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

Java 8之前的寫(xiě)法:

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

另一個(gè)新需求

可能想要選出重量超過(guò)150克的蘋(píng)果

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

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


Java 8會(huì)把條件代碼作為參數(shù)傳遞進(jìn)去,這樣可以避免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<Apple> filterApples(List<Apple> inventory, java.util.function.Predicate<Apple> p) {
	List<Apple> result = new ArrayList<>();
	for (Apple apple : inventory) {
		if (p.test(apple)) {
			result.add(apple);
		}
	}
	return result;
}

要用它的話,可以寫(xiě)成

filterApples(inventory, FilteringApples::isGreenApple);

filterApples(inventory, FilteringApples::isHeavyApple);

什么是謂詞Predicate?

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

從傳遞方法到Lambda

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

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

都不需要為只用一次的方法寫(xiě)定義;代碼更干凈、更清晰,因?yàn)槟阌貌恢フ易约旱降讉鬟f了什么代碼。

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

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

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

static <T> Collection<T> filter(Collection<T> c, Predicate<T> p);

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

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

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

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

需求

Java 8前

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

Map<Currency, List<Transaction>> transactionsByCurrencies = new HashMap<>();

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

		List<Transaction> 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<Currency, List<Transaction>> transactionsByCurrencies = transactions.stream()
		.filter((Transaction t) -> t.getPrice() > 1000)
		.collect(groupingBy(Transaction::getCurrency));

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

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

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

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

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

多線程并非易事

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

譬如:線程可能會(huì)同時(shí)訪問(wèn)并更新共享變量。

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

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

Java8有哪些特性

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

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

第二個(gè)原因是,這類操作常常可以并行化。

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

Java8有哪些特性

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

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

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

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

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

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

其次,流提供的這個(gè)幾乎免費(fèi)的并行,只有在傳遞給filter之類的庫(kù)方法的方法不會(huì)互動(dòng)(比方說(shuō)有可變的共享對(duì)象)時(shí)才能工作。

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

默認(rèn)方法

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

這一方法很重要,因?yàn)槟銜?huì)在接口中遇到越來(lái)越多的默認(rèn)方法,但由于真正需要編寫(xiě)默認(rèn)方法的程序員相對(duì)較少,而且它們只是有助于程序改進(jìn),而不是用于編寫(xiě)任何具體的程序


譬如

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

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

這里有個(gè)問(wèn)題:在Java 8之前,List<T>并沒(méi)有stream或parallelStream方法,它實(shí)現(xiàn)的Collection<T>接口也沒(méi)有,因?yàn)楫?dāng)初還沒(méi)有想到這些方法嘛!可沒(méi)有這些方法,這些代碼就不能編譯

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

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

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

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

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

default void sort(Comparator<? super E> c) {
	Collections.sort(this, c);
}

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

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

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

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI