JAVA版本總結

給自己總結一個所有JAVA版本更新的核心內(nèi)容總結:

JAVA 8?

JAVA 8在java更新的歷史中是一個非常重要的一個版本捏萍,引入函數(shù)式編程使得java編程更為強大旷余,主要更新的內(nèi)容:

? ?1.Lambda表達式

Lambda表達式(也叫做閉包)是Java 8中最大的也是期待已久的變化。它允許我們將一個函數(shù)當作方法的參數(shù)(傳遞函數(shù))薪鹦,或者說把代碼當作數(shù)據(jù)掌敬,這是每個函數(shù)式編程者熟悉的概念。很多基于JVM平臺的語言一開始就支持Lambda表達式池磁,但是Java程序員沒有選擇奔害,只能使用匿名內(nèi)部類來替代Lambda表達式。

Lambda表達式的設計被討論了很久地熄,而且花費了很多的功夫來交流华临。不過最后取得了一個折中的辦法,得到了一個新的簡明并且緊湊的Lambda表達式結構端考。最簡單的Lambda表達式可以用逗號分隔的參數(shù)列表雅潭、->符號和功能語句塊來表示揭厚。示例如下:

Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) );

請注意到編譯器會根據(jù)上下文來推測參數(shù)的類型,或者你也可以顯示地指定參數(shù)類型扶供,只需要將類型包在括號里筛圆。舉個例子:

Arrays.asList( "a", "b", "d" ).forEach( ( String e ) -> System.out.println( e ) );

如果Lambda的功能語句塊太復雜,我們可以用大括號包起來椿浓,跟普通的Java方法一樣太援,如下:

String separator = ",";

Arrays.asList( "a", "b", "d" ).forEach(

? ? ( String e ) -> System.out.print( e + separator ) );

Lambda表達式可能會引用類的成員或者局部變量(會被隱式地轉變成final類型)一铅,下面兩種寫法的效果是一樣的:

String separator = ",";

Arrays.asList( "a", "b", "d" ).forEach(

? ? ( String e ) -> System.out.print( e + separator ) );

final String separator = ",";

Arrays.asList( "a", "b", "d" ).forEach(

? ? ( String e ) -> System.out.print( e + separator ) );

Lambda表達式可能會有返回值色乾,編譯器會根據(jù)上下文推斷返回值的類型。如果lambda的語句塊只有一行主巍,不需要return關鍵字笋敞。下面兩個寫法是等價的:

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;

} );

語言的設計者們思考了很多如何讓現(xiàn)有的功能和lambda表達式友好兼容唧垦。于是就有了函數(shù)接口這個概念。函數(shù)接口是一種只有一個方法的接口液样,像這樣地振亮,函數(shù)接口可以隱式地轉換成lambda表達式。

java.lang.Runnable 和java.util.concurrent.Callable是函數(shù)接口兩個最好的例子鞭莽。但是在實踐中坊秸,函數(shù)接口是非常脆弱的,只要有人在接口里添加多一個方法澎怒,那么這個接口就不是函數(shù)接口了褒搔,就會導致編譯失敗。Java 8提供了一個特殊的注解@FunctionalInterface來克服上面提到的脆弱性并且顯示地表明函數(shù)接口的目的(java里所有現(xiàn)存的接口都已經(jīng)加上了@FunctionalInterface)喷面。讓我們看看一個簡單的函數(shù)接口定義:

@FunctionalInterface

public interface Functional {

? ? void method();

}

我們要記住默認的方法和靜態(tài)方法(下一節(jié)會具體解釋)不會違反函數(shù)接口的約定星瘾,例子如下:

@FunctionalInterface

public interface FunctionalDefaultMethods {

? ? void method();

? ? default void defaultMethod() {

? ? }

}

支持Lambda是Java 8最大的賣點,他有巨大的潛力吸引越來越多的開發(fā)人員轉到這個開發(fā)平臺來惧辈,并且在純Java里提供最新的函數(shù)式編程的概念琳状。

? ? 2.新的日期API

Java 8引入了新的日期時間API(JSR 310)改進了日期時間的管理。日期和時間管理一直是Java開發(fā)人員最痛苦的問題盒齿。java.util.Date和后來的java.util.Calendar一點也沒有改變這個情況(甚至讓人們更加迷茫)念逞。

因為上面這些原因,產(chǎn)生了Joda-Time边翁,可以替換Java的日期時間API翎承。Joda-Time深刻影響了 Java 8新的日期時間API,Java 8吸收了Joda-Time?的精華符匾。新的java.time包包含了所有關于日期叨咖、時間、日期時間、時區(qū)甸各、Instant(跟日期類似但精確到納秒)仰剿、duration(持續(xù)時間)和時鐘操作的類。設計這些API的時候很認真地考慮了這些類的不變性(從java.util.Calendar吸取的痛苦教訓)痴晦。如果需要修改時間對象南吮,會返回一個新的實例。

讓我們看看一些關鍵的類和用法示例誊酌。第一個類是Clock部凑,Clock使用時區(qū)來訪問當前的instant, date和time。Clock類可以替換System.currentTimeMillis()TimeZone.getDefault().

1// Get the system clock as UTC offset

2final?Clock clock = Clock.systemUTC();

3System.out.println( clock.instant() );

4System.out.println( clock.millis() );

控制臺輸出如下:

2014-04-12T15:19:29.282Z

1397315969360

其他類我們看看LocalTime和LocalDate碧浊。LocalDate只保存有ISO-8601日期系統(tǒng)的日期部分涂邀,有時區(qū)信息,相應地箱锐,LocalTime只保存ISO-8601日期系統(tǒng)的時間部分比勉,沒有時區(qū)信息。LocalDate和LocalTime都可以從Clock對象創(chuàng)建驹止。

01// Get the local date and local time

02final?LocalDate date = LocalDate.now();

03final?LocalDate dateFromClock = LocalDate.now( clock );

04?

05System.out.println( date );

06System.out.println( dateFromClock );

07?

08// Get the local date and local time

09final?LocalTime time = LocalTime.now();

10final?LocalTime timeFromClock = LocalTime.now( clock );

11?

12System.out.println( time );

13System.out.println( timeFromClock );

控制臺輸出如下:

2014-04-12

2014-04-12

11:25:54.568

15:25:54.568

LocalDateTime類合并了LocalDate和LocalTime浩聋,它保存有ISO-8601日期系統(tǒng)的日期和時間,但是沒有時區(qū)信息臊恋。讓我們看一個簡單的例子衣洁。

1// Get the local date/time

2final?LocalDateTime datetime = LocalDateTime.now();

3final?LocalDateTime datetimeFromClock = LocalDateTime.now( clock );

4?

5System.out.println( datetime );

6System.out.println( datetimeFromClock );

輸出如下:

2014-04-12T11:37:52.309

2014-04-12T15:37:52.309

如果您需要一個類持有日期時間和時區(qū)信息,可以使用ZonedDateTime抖仅,它保存有ISO-8601日期系統(tǒng)的日期和時間坊夫,而且有時區(qū)信息。讓我們看一些例子:

1// Get the zoned date/time

2final?ZonedDateTime zonedDatetime = ZonedDateTime.now();

3final?ZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now( clock );

4final?ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now( ZoneId.of(?"America/Los_Angeles"?) );

5?

6System.out.println( zonedDatetime );

7System.out.println( zonedDatetimeFromClock );

8System.out.println( zonedDatetimeFromZone );

輸出如下:

2014-04-12T11:47:01.017-04:00[America/New_York]

2014-04-12T15:47:01.017Z

2014-04-12T08:47:01.017-07:00[America/Los_Angeles]

最后讓我們看看Duration類撤卢,Duration持有的時間精確到納秒环凿。它讓我們很容易計算兩個日期中間的差異。讓我們來看一下:

1// Get duration between two dates

2final?LocalDateTime from = LocalDateTime.of(?2014, Month.APRIL,?16,?0,?0,?0?);

3final?LocalDateTime to = LocalDateTime.of(?2015, Month.APRIL,?16,?23,?59,?59?);

4?

5final?Duration duration = Duration.between( from, to );

6System.out.println(?"Duration in days: "?+ duration.toDays() );

7System.out.println(?"Duration in hours: "?+ duration.toHours() );

上面的例子計算了兩個日期(2014年4月16日和2014年5月16日)之間的持續(xù)時間(基于天數(shù)和小時)輸出如下:

Duration in days: 365

Duration in hours: 8783

對于Java 8的新日期時間的總體印象還是比較積極的放吩。一部分是因為有經(jīng)歷實戰(zhàn)的Joda-Time的基礎智听,還有一部分是因為日期時間終于被認真對待而且聽取了開發(fā)人員的聲音。

? ? 3.引入Optional

著名的NullPointerException是引起系統(tǒng)失敗最常見的原因屎慢。很久以前Google Guava項目引入了Optional作為解決空指針異常的一種方式瞭稼,不贊成代碼被null檢查的代碼污染,期望程序員寫整潔的代碼腻惠。受Google Guava的鼓勵,Optional現(xiàn)在是Java 8庫的一部分欲虚。

Optional只是一個容器集灌,它可以保存一些類型的值或者null。它提供很多有用的方法,所以沒有理由不顯式地檢查null欣喧。請參照java 8的文檔查看詳細信息腌零。

讓我們看看兩個Optional用法的小例子:一個是允許為空的值,另外一個是不允許為空的值唆阿。

1Optional< String > fullName = Optional.ofNullable(?null?);

2System.out.println(?"Full Name is set? "?+ fullName.isPresent() );???????

3System.out.println(?"Full Name: "?+ fullName.orElseGet( () ->?"[none]"?) );

4System.out.println( fullName.map( s ->?"Hey "?+ s +?"!"?).orElse(?"Hey Stranger!"?) );

如果Optional實例有非空的值益涧,方法?isPresent()?返回true否則返回false。方法orElseGet提供了回退機制驯鳖,當Optional的值為空時接受一個方法返回默認值闲询。map()方法轉化Optional當前的值并且返回一個新的Optional實例。orElse方法和orElseGet類似浅辙,但是它不接受一個方法扭弧,而是接受一個默認值。上面代碼運行結果如下:

Full Name is set? false

Full Name: [none]

Hey Stranger!

讓我們大概看看另外一個例子记舆。

1Optional< String > firstName = Optional.of(?"Tom"?);

2System.out.println(?"First Name is set? "?+ firstName.isPresent() );???????

3System.out.println(?"First Name: "?+ firstName.orElseGet( () ->?"[none]"?) );

4System.out.println( firstName.map( s ->?"Hey "?+ s +?"!"?).orElse(?"Hey Stranger!"?) );

5System.out.println();

輸出如下:

First Name is set? true

First Name: Tom

Hey Tom!

? ? 4.使用Base64

? ? 5.接口的默認方法和靜態(tài)方法

Java 8增加了兩個新的概念在接口聲明的時候:默認和靜態(tài)方法鸽捻。默認方法和Trait有些類似,但是目標不一樣泽腮。默認方法允許我們在接口里添加新的方法御蒲,而不會破壞實現(xiàn)這個接口的已有類的兼容性,也就是說不會強迫實現(xiàn)接口的類實現(xiàn)默認方法诊赊。

默認方法和抽象方法的區(qū)別是抽象方法必須要被實現(xiàn)删咱,默認方法不是。作為替代方式豪筝,接口可以提供一個默認的方法實現(xiàn)痰滋,所有這個接口的實現(xiàn)類都會通過繼承得倒這個方法(如果有需要也可以重寫這個方法),讓我們來看看下面的例子:

01private?interface?Defaulable {

02????// Interfaces now allow default methods, the implementer may or

03????// may not implement (override) them.

04????default?String notRequired() {

05????????return?"Default implementation";

06????}

07}

08?

09private?static?class?DefaultableImpl?implements?Defaulable {

10}

11?

12private?static?class?OverridableImpl?implements?Defaulable {

13????@Override

14????public?String notRequired() {

15????????return?"Overridden implementation";

16????}

17}

接口Defaulable使用default關鍵字聲明了一個默認方法notRequired()续崖,類DefaultableImpl實現(xiàn)了Defaulable接口敲街,沒有對默認方法做任何修改。另外一個類OverridableImpl重寫類默認實現(xiàn)严望,提供了自己的實現(xiàn)方法多艇。

Java 8 的另外一個有意思的新特性是接口里可以聲明靜態(tài)方法,并且可以實現(xiàn)像吻。例子如下:

1private?interface?DefaulableFactory {

2????// Interfaces now allow static methods

3????static?Defaulable create( Supplier< Defaulable > supplier ) {

4????????return?supplier.get();

5????}

6}

下面是把接口的靜態(tài)方法和默認方法放在一起的示例(::new?是構造方法引用峻黍,后面會有詳細描述):

1public?static?void?main( String[] args ) {

2????Defaulable defaulable = DefaulableFactory.create( DefaultableImpl::new?);

3????System.out.println( defaulable.notRequired() );

4?

5????defaulable = DefaulableFactory.create( OverridableImpl::new?);

6????System.out.println( defaulable.notRequired() );

7}

控制臺的輸出如下:

Default implementation

Overridden implementation

JVM平臺的接口的默認方法實現(xiàn)是很高效的,并且方法調用的字節(jié)碼指令支持默認方法拨匆。默認方法使已經(jīng)存在的接口可以修改而不會影響編譯的過程姆涩。java.util.Collection中添加的額外方法就是最好的例子:stream(),parallelStream(),forEach(),removeIf()

雖然默認方法很強大,但是使用之前一定要仔細考慮是不是真的需要使用默認方法惭每,因為在層級很復雜的情況下很容易引起模糊不清甚至變異錯誤骨饿。

? ? 6.新增方法引用格式

方法引用提供了一個很有用的語義來直接訪問類或者實例的已經(jīng)存在的方法或者構造方法。結合Lambda表達式,方法引用使語法結構緊湊簡明宏赘。不需要復雜的引用绒北。

下面我們用Car?這個類來做示例,Car這個類有不同的方法定義察署。讓我們來看看java 8支持的4種方法引用闷游。

01public?static?class?Car {

02????public?static?Car create(?final?Supplier< Car > supplier ) {

03????????return?supplier.get();

04????}?????????????

05?

06????public?static?void?collide(?final?Car car ) {

07????????System.out.println(?"Collided "?+ car.toString() );

08????}

09?

10????public?void?follow(?final?Car another ) {

11????????System.out.println(?"Following the "?+ another.toString() );

12????}

13?

14????public?void?repair() {

15????????System.out.println(?"Repaired "?+?this.toString() );

16????}

17}

第一種方法引用是構造方法引用,語法是:Class::new贴汪,對于泛型來說語法是:Class<T >::new脐往,請注意構造方法沒有參數(shù):

1final?Car car = Car.create( Car::new?);

2final?List< Car > cars = Arrays.asList( car );

第二種方法引用是靜態(tài)方法引用,語法是:Class::static_method請注意這個靜態(tài)方法只支持一個類型為Car的參數(shù)嘶是。

1cars.forEach( Car::collide );

第三種方法引用是類實例的方法引用钙勃,語法是:Class::method請注意方法沒有參數(shù)。

1cars.forEach( Car::repair );

最后一種方法引用是引用特殊類的方法聂喇,語法是:instance::method辖源,請注意只接受Car類型的一個參數(shù)。

1final?Car police = Car.create( Car::new?);

2cars.forEach( police::follow );

運行這些例子我們將會在控制臺得到如下信息(Car的實例可能會不一樣):?

Collided com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d

Repaired com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d

Following the com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d

關于方法引用更多的示例和詳細信息希太,請參考官方文檔

? ? 7.新增Stream類

新增加的Stream API?(java.util.stream)引入了在Java里可以工作的函數(shù)式編程克饶。這是目前為止對java庫最大的一次功能添加,希望程序員通過編寫有效誊辉、整潔和簡明的代碼矾湃,能夠大大提高生產(chǎn)率。

Stream API讓集合處理簡化了很多(我們后面會看到不僅限于Java集合類)堕澄。讓我們從一個簡單的類Task開始來看看Stream的用法邀跃。

01public?class?Streams {

02private?enum?Status {

03OPEN, CLOSED

04};

05?

06private?static?final?class?Task {

07private?final?Status status;

08private?final?Integer points;

09?

10Task(?final?Status status,?final?Integer points ) {

11this.status = status;

12this.points = points;

13}

14?

15public?Integer getPoints() {

16return?points;

17}

18?

19public?Status getStatus() {

20return?status;

21}

22?

23@Override

24public?String toString() {

25return?String.format(?"[%s, %d]", status, points );

26}

27}

28}

Task類有一個分數(shù)的概念(或者說是偽復雜度),其次是還有一個值可以為OPEN或CLOSED的狀態(tài).讓我們引入一個Task的小集合作為演示例子:

1final?Collection< Task > tasks = Arrays.asList(

2????new?Task( Status.OPEN,?5?),

3????new?Task( Status.OPEN,?13?),

4????new?Task( Status.CLOSED,?8?)

5);

第一個問題是所有的開放的Task的點數(shù)是多少蛙紫?在java 8 之前拍屑,通常的做法是用foreach迭代。但是Java8里頭我們會用Stream坑傅。Stream是多個元素的序列僵驰,支持串行和并行操作。

1// Calculate total points of all active tasks using sum()

2final?long?totalPointsOfOpenTasks = tasks

3????.stream()

4????.filter( task -> task.getStatus() == Status.OPEN )

5????.mapToInt( Task::getPoints )

6????.sum();

7?????????

8System.out.println(?"Total points: "?+ totalPointsOfOpenTasks );

控制臺的輸出將會是:

Total points: 18

上面代碼執(zhí)行的流程是這樣的唁毒,首先Task集合會被轉化為Stream表示蒜茴,然后filter操作會過濾掉所有關閉的Task,接下來使用Task::getPoints?方法取得每個Task實例的點數(shù)浆西,mapToInt方法會把Task Stream轉換成Integer Stream粉私,最后使用Sum方法將所有的點數(shù)加起來得到最終的結果。

在我們看下一個例子之前室谚,我們要記住一些關于Stream的說明毡鉴。Stream操作被分為中間操作和終點操作崔泵。

中間操作返回一個新的Stream秒赤。這些中間操作是延遲的猪瞬,執(zhí)行一個中間操作比如filter實際上不會真的做過濾操作,而是創(chuàng)建一個新的Stream入篮,當這個新的Stream被遍歷的時候陈瘦,它里頭會包含有原來Stream里符合過濾條件的元素。

終點操作比如說forEach或者sum會遍歷Stream從而產(chǎn)生最終結果或附帶結果潮售。終點操作執(zhí)行完之后痊项,Stream管道就被消費完了,不再可用酥诽。在幾乎所有的情況下鞍泉,終點操作都是即時完成對數(shù)據(jù)的遍歷操作。

Stream的另外一個價值是Stream創(chuàng)造性地支持并行處理肮帐。讓我們看看下面這個例子咖驮,這個例子把所有task的點數(shù)加起來。

1// Calculate total points of all tasks

2final?double?totalPoints = tasks

3???.stream()

4???.parallel()

5???.map( task -> task.getPoints() )?// or map( Task::getPoints )

6???.reduce(?0, Integer::sum );

7????

8System.out.println(?"Total points (all tasks): "?+ totalPoints );

這個例子跟上面那個非常像训枢,除了這個例子里使用了parallel()方法?????? 并且計算最終結果的時候使用了reduce方法托修。

輸出如下:

Total points (all tasks): 26.0

經(jīng)常會有這個一個需求:我們需要按照某種準則來對集合中的元素進行分組。Stream也可以處理這樣的需求恒界,下面是一個例子:

1// Group tasks by their status

2final?Map< Status, List< Task > > map = tasks

3????.stream()

4????.collect( Collectors.groupingBy( Task::getStatus ) );

5System.out.println( map );

控制臺的輸出如下:

{CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]}

讓我們來計算整個集合中每個task分數(shù)(或權重)的平均值來結束task的例子睦刃。

01// Calculate the weight of each tasks (as percent of total points)

02final?Collection< String > result = tasks

03????.stream()?// Stream< String >

04????.mapToInt( Task::getPoints )?// IntStream

05????.asLongStream()?// LongStream

06????.mapToDouble( points -> points / totalPoints )?// DoubleStream

07????.boxed()?// Stream< Double >

08????.mapToLong( weigth -> (?long?)( weigth *?100?) )?// LongStream

09????.mapToObj( percentage -> percentage +?"%"?)?// Stream< String>

10????.collect( Collectors.toList() );?// List< String >

11?????????

12System.out.println( result );

控制臺輸出如下:

[19%, 50%, 30%]

最后,就像前面提到的十酣,Stream API不僅僅處理Java集合框架涩拙。像從文本文件中逐行讀取數(shù)據(jù)這樣典型的I/O操作也很適合用Stream API來處理。下面用一個例子來應證這一點耸采。

1final?Path path =?new?File( filename ).toPath();

2try( Stream< String > lines = Files.lines( path, StandardCharsets.UTF_8 ) ) {

3????lines.onClose( () -> System.out.println("Done!") ).forEach( System.out::println );

4}

Stream的方法onClose返回一個等價的有額外句柄的Stream兴泥,當Stream的close()方法被調用的時候這個句柄會被執(zhí)行。

Stream API洋幻、Lambda表達式還有接口默認方法和靜態(tài)方法支持的方法引用郁轻,是Java 8對軟件開發(fā)的現(xiàn)代范式的響應。

? ? 8.注解相關的改變

? ? 9.支持并行(parallel)數(shù)組

Java 8新增加了很多方法支持并行的數(shù)組處理文留。最重要的大概是parallelSort()這個方法顯著地使排序在多核計算機上速度加快好唯。下面的小例子演示了這個新的方法(parallelXXX)的行為。

01</pre>

02

03?

04import?java.util.Arrays;

05import?java.util.concurrent.ThreadLocalRandom;

06?

07public?class?ParallelArrays {

08????public?static?void?main( String[] args ) {

09????????long[] arrayOfLong =?new?long?[?20000?];???????

10?

11????????Arrays.parallelSetAll( arrayOfLong,

12????????????index -> ThreadLocalRandom.current().nextInt(?1000000?) );

13????????Arrays.stream( arrayOfLong ).limit(?10?).forEach(

14????????????i -> System.out.print( i +?" "?) );

15????????System.out.println();

16?

17????????Arrays.parallelSort( arrayOfLong );

18????????Arrays.stream( arrayOfLong ).limit(?10?).forEach(

19????????????i -> System.out.print( i +?" "?) );

20????????System.out.println();

21????}

22}</pre>

23<pre>

這一小段代碼使用parallelSetAll()t方法填充這個長度是2000的數(shù)組燥翅,然后使用parallelSort()排序骑篙。這個程序輸出了排序前和排序后的10個數(shù)字來驗證數(shù)組真的已經(jīng)被排序了。示例可能的輸出如下(請注意這些數(shù)字是隨機產(chǎn)生的)

Unsorted: 591217 891976 443951 424479 766825 351964 242997 642839 119108 552378

Sorted: 39 220 263 268 325 607 655 678 723 793

? ? 10.對并發(fā)類(Concurrency)的擴展森书。

在新增Stream機制與lambda的基礎之上靶端,在java.util.concurrent.ConcurrentHashMap中加入了一些新方法來支持聚集操作谎势。同時也在java.util.concurrent.ForkJoinPool類中加入了一些新方法來支持共有資源池(common pool)

新增的java.util.concurrent.locks.StampedLock類提供一直基于容量的鎖,這種鎖有三個模型來控制讀寫操作(它被認為是不太有名的java.util.concurrent.locks.ReadWriteLock類的替代者)杨名。

在java.util.concurrent.atomic包中還增加了下面這些類:

DoubleAccumulator

DoubleAdder

LongAccumulator

LongAdder

JAVA 9

Java 9 發(fā)布于 2017 年 9 月 22 日脏榆,帶來了很多新特性,其中最主要的變化是已經(jīng)實現(xiàn)的模塊化系統(tǒng)台谍。

1 模塊系統(tǒng):模塊是一個包的容器须喂,Java 9 最大的變化之一是引入了模塊系統(tǒng)(Jigsaw 項目)。

2 REPL (JShell):交互式編程環(huán)境趁蕊。

JAVA 10

1. 局部變量類型推斷

局部變量類型推斷可以說是Java 10中最值得注意的特性坞生,這是Java語言開發(fā)人員為了簡化Java應用程序的編寫而采取的又一步,如下圖所示掷伙。

局部變量類型推斷將引入"var"關鍵字是己,也就是你可以隨意定義變量而不必指定變量的類型

局部變量類型推薦僅限于如下使用場景:

局部變量初始化

for循環(huán)內(nèi)部索引變量

傳統(tǒng)的for循環(huán)聲明變量

Java官方表示,它不能用于以下幾個地方:

方法參數(shù)

構造函數(shù)

參數(shù)方法返回

類型字段捕獲表達式(或任何其他類型的變量聲明)

2 JEP304任柜,統(tǒng)一的垃圾回收接口卒废。

3 JEP307,G1 垃圾回收器的并行完整垃圾回收乘盼,實現(xiàn)并行性來改善最壞情況下的延遲升熊。

JAVA 11

1 本地變量類型推斷

2、字符串加強

Java 11 增加了一系列的字符串處理方法绸栅,如以下所示级野。

// 判斷字符串是否為空白" ".isBlank(); // true// 去除首尾空格" Javastack ".strip(); // "Javastack"http:// 去除尾部空格?" Javastack ".stripTrailing(); // " Javastack"http:// 去除首部空格?" Javastack ".stripLeading(); // "Javastack "http:// 復制字符串"Java".repeat(3); // "JavaJavaJava"http:// 行數(shù)統(tǒng)計"A\nB\nC".lines().count(); // 3

3、集合加強

自 Java 9 開始粹胯,Jdk 里面為集合(List/ Set/ Map)都添加了of和copyOf方法蓖柔,它們兩個都用來創(chuàng)建不可變的集合,來看下它們的使用和區(qū)別风纠。

示例1:

var list = List.of("Java", "Python", "C");var copy = List.copyOf(list);System.out.println(list == copy); // true

示例2:

var list = new ArrayList<String>();var copy = List.copyOf(list);System.out.println(list == copy); // false

示例1和2代碼差不多况鸣,為什么一個為true,一個為false?

來看下它們的源碼:

static <E> List<E> of(E... elements) {switch (elements.length) { // implicit null check of elements?case 0:?return ImmutableCollections.emptyList();?case 1:?return new ImmutableCollections.List12<>(elements[0]);?case 2:?return new ImmutableCollections.List12<>(elements[0], elements[1]);?default:?return new ImmutableCollections.ListN<>(elements);?}}static <E> List<E> copyOf(Collection<? extends E> coll) {?return ImmutableCollections.listCopy(coll);}static <E> List<E> listCopy(Collection<? extends E> coll) {?if (coll instanceof AbstractImmutableList && coll.getClass() != SubList.class) {?return (List)coll;?} else {?return (List)List.of(coll.toArray());?}}

可以看出copyOf方法會先判斷來源集合是不是AbstractImmutableList類型的,如果是竹观,就直接返回镐捧,如果不是,則調用of創(chuàng)建一個新的集合臭增。

示例2因為用的 new 創(chuàng)建的集合懂酱,不屬于不可變AbstractImmutableList類的子類,所以copyOf方法又創(chuàng)建了一個新的實例誊抛,所以為false.

注意:使用 of 和 copyOf 創(chuàng)建的集合為不可變集合列牺,不能進行添加、刪除拗窃、替換瞎领、排序等操作泌辫,不然會報

java.lang.UnsupportedOperationException異常。

上面演示了 List 的 of 和 copyOf 方法九默,Set 和 Map 接口都有震放。

4、Stream 加強

Stream 是 Java 8 中的新特性荤西,Java 9 開始對 Stream 增加了以下 4 個新方法澜搅。

增加單個參數(shù)構造方法伍俘,可為null

Stream.ofNullable(null).count(); // 0

增加 takeWhile 和 dropWhile 方法

Stream.of(1, 2, 3, 2, 1).takeWhile(n -> n < 3)?.collect(Collectors.toList()); // [1, 2]

從開始計算邪锌,當 n < 3 時就截止。

Stream.of(1, 2, 3, 2, 1).dropWhile(n -> n < 3)?.collect(Collectors.toList()); // [3, 2, 1]

這個和上面的相反癌瘾,一旦 n < 3 不成立就開始計算觅丰。

3)iterate重載

這個 iterate 方法的新重載方法,可以讓你提供一個 Predicate (判斷條件)來指定什么時候結束迭代妨退。

如果你對 JDK 8 中的 Stream 還不熟悉妇萄,可以看之前分享的這一系列教程。

5咬荷、Optional 加強

Opthonal 也增加了幾個非彻诰洌酷的方法,現(xiàn)在可以很方便的將一個 Optional 轉換成一個 Stream, 或者當一個空 Optional 時給它一個替代的幸乒。

Optional.of("javastack").orElseThrow(); // javastackOptional.of("javastack").stream().count(); // 1Optional.ofNullable(null).or(() -> Optional.of("javastack"))?.get(); // javastack

6懦底、InputStream 加強

InputStream 終于有了一個非常有用的方法:transferTo,可以用來將數(shù)據(jù)直接傳輸?shù)?OutputStream罕扎,這是在處理原始數(shù)據(jù)流時非常常見的一種用法聚唐,如下示例。

var classLoader = ClassLoader.getSystemClassLoader();var inputStream = classLoader.getResourceAsStream("javastack.txt");var javastack = File.createTempFile("javastack2", "txt");try (var outputStream = new FileOutputStream(javastack)) {inputStream.transferTo(outputStream);}

7腔召、HTTP Client API

這是 Java 9 開始引入的一個處理 HTTP 請求的的孵化 HTTP Client API杆查,該 API 支持同步和異步,而在 Java 11 中已經(jīng)為正式可用狀態(tài)臀蛛,你可以在

java.net

包中找到這個 API亲桦。

來看一下 HTTP Client 的用法:

var request = HttpRequest.newBuilder().uri(URI.create("https://javastack.cn"))?.GET()?.build();var client = HttpClient.newHttpClient();// 同步HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());System.out.println(response.body());// 異步client.sendAsync(request, HttpResponse.BodyHandlers.ofString())?.thenApply(HttpResponse::body)?.thenAccept(System.out::println);

上面的.GET()可以省略,默認請求方式為 Get浊仆!

更多使用示例可以看這個 API客峭,后續(xù)有機會再做演示。

現(xiàn)在 Java 自帶了這個 HTTP Client API氧卧,我們以后還有必要用 Apache 的 HttpClient 工具包嗎桃笙?

8、化繁為簡沙绝,一個命令編譯運行源代碼

看下面的代碼搏明。

// 編譯javac Javastack.java// 運行java Javastack

在我們的認知里面鼠锈,要運行一個 Java 源代碼必須先編譯,再運行星著,兩步執(zhí)行動作购笆。而在未來的 Java 11 版本中,通過一個java命令就直接搞定了虚循,如以下所示同欠。

javaJavastack.java

JAVA 12

1、Shenandoah:低暫停時間的 GC(實驗性功能)

新增了一個名為 Shenandoah 的?GC 算法横缔,通過與正在運行的 Java 線程同時進行 evacuation 工作來減少 GC 暫停時間铺遂。使用 Shenandoah 的暫停時間與堆大小無關,這意味著無論堆是 200 MB 還是 200 GB茎刚,都將具有相同的暫停時間襟锐。

2、微基準測試套件

JDK 源碼中新增了一套微基準測試套件膛锭,使開發(fā)人員可以輕松運行現(xiàn)有的微基準測試并創(chuàng)建新的基準測試粮坞。

3、Switch 表達式(預覽功能)

擴展了 switch 語句初狰,使其不僅可以作為語句(statement)莫杈,還可以作為表達式(expression),并且兩種寫法都可以使用傳統(tǒng)的 switch 語法奢入,或者使用簡化的“case L ->”模式匹配語法作用于不同范圍并控制執(zhí)行流筝闹。這些更改將簡化日常編碼工作,并為 switch 中的模式匹配(JEP 305)做好準備俊马。

4丁存、JVM 常量 API

引入 API 對關鍵類文件和運行時工件建模,特別是可從常量池加載的常量柴我。在新的 java.lang.invoke.constant 包中定義了一系列基于值的符號引用(JVMS 5.1)類型解寝,它們能夠描述每種可加載常量。

符號引用以純?nominal 形式描述可加載常量艘儒,與類加載或可訪問性上下文區(qū)分開聋伦。有些類可以作為自己的符號引用(例如 String),而對于可鏈接常量界睁,定義了一系列符號引用類型(ClassDesc觉增、MethodTypeDesc、MethodHandleDesc 和 DynamicConstantDesc)翻斟,它們包含描述這些常量的 nominal 信息逾礁。

5、只保留一個 AArch64 實現(xiàn)

刪除了與 arm64 相關的所有源访惜,同時保留 32 位 ARM 實現(xiàn)和 64 位 aarch64嘹履。

JDK 中存在兩套?64 位 ARM 實現(xiàn)腻扇,主要存在于 src/hotspot/cpu/arm 和 open/src/hotspot/cpu/aarch64 目錄。兩者都實現(xiàn)了?aarch64砾嫉,現(xiàn)在將只保留后者幼苛,刪除由?Oracle 提供的 arm64。這將使貢獻者將他們的精力集中在單個 64 位 ARM 實現(xiàn)上焕刮,并消除維護兩套實現(xiàn)所需的重復工作舶沿。

6、默認類數(shù)據(jù)共享歸檔文件

針對 64 位平臺配并,使用默認類列表增強 JDK 構建過程以生成類數(shù)據(jù)共享(class data-sharing括荡,CDS)檔。

7荐绝、可中止的 G1 Mixed GC

如果 G1 Mixed GC 存在超出暫停目標的可能性一汽,則使其可中止。

8低滩、G1 及時返回未使用的已分配內(nèi)存

增強 G1 GC,在空閑時自動將 Java 堆內(nèi)存返回給操作系統(tǒng)岩喷。為了實現(xiàn)向操作系統(tǒng)返回最大內(nèi)存量的目標恕沫,G1 將在應用程序不活動期間定期執(zhí)行或觸發(fā)并發(fā)周期以確定整體 Java 堆使用情況。這將導致它自動將 Java 堆的未使用部分返回給操作系統(tǒng)纱意。而在用戶控制下婶溯,可以可選地執(zhí)行完整的 GC,以使返回的內(nèi)存量最大化偷霉。

JAVA 13

1迄委、Dynamic CDS Archives

這一特性是在JEP310:Application Class-Data Sharing 基礎上擴展而來的,Dynamic CDS Archives中的CDS指的就是Class-Data Sharing类少。

那么叙身,這個JEP310是個啥東西呢?

我們知道在同一個物理機/虛擬機上啟動多個JVM時硫狞,如果每個虛擬機都單獨裝載自己需要的所有類信轿,啟動成本和內(nèi)存占用是比較高的。所以Java團隊引入了CDS的概念残吩,通過把一些核心類在每個JVM間共享财忽,每個JVM只需要裝載自己的應用類,啟動時間減少了泣侮,另外核心類是共享的即彪,所以JVM的內(nèi)存占用也減少了。

CDS 只能作用于 Boot Class Loader 加載的類活尊,不能作用于 App Class Loader 或者自定義的 Class Loader 加載的類隶校。

在 Java 10 中琼蚯,則將 CDS 擴展為 AppCDS,顧名思義惠况,AppCDS 不止能夠作用于 Boot Class Loader了遭庶,App Class Loader 和自定義的 Class Loader 也都能夠起作用,大大加大了 CDS 的適用范圍稠屠。也就說開發(fā)自定義的類也可以裝載給多個JVM共享了峦睡。

Java 10中包含的JEP310的通過跨不同Java進程共享公共類元數(shù)據(jù)來減少了內(nèi)存占用和改進了啟動時間。

但是权埠,JEP310中榨了,使用AppCDS的過程還是比較復雜的,需要有三個步驟:

1攘蔽、決定要 Dump 哪些 Class

2龙屉、將類的內(nèi)存 Dump 到歸檔文件中

3、使用 Dump 出來的歸檔文件加快應用啟動速度

這一次的JDK 13中的JEP 350 满俗,在JEP310的基礎上转捕,又做了一些擴展。允許在Java應用程序執(zhí)行結束時動態(tài)歸檔類唆垃,歸檔類將包括默認的基礎層 CDS(class data-sharing)存檔中不存在的所有已加載的應用程序類和庫類五芝。

也就是說,在Java 13中再使用AppCDS的時候辕万,就不在需要這么復雜了枢步。

2、ZGC: Uncommit Unused Memory

在討論這個問題之前渐尿,想先問一個問題醉途,JVM的GC釋放的內(nèi)存會還給操作系統(tǒng)嗎?

GC后的內(nèi)存如何處置砖茸,其實是取決于不同的垃圾回收器的隘擎。因為把內(nèi)存還給OS,意味著要調整JVM的堆大小渔彰,這個過程是比較耗費資源的嵌屎。

在JDK 11中,Java引入了ZGC恍涂,這是一款可伸縮的低延遲垃圾收集器宝惰,但是當時只是實驗性的。并且再沧,ZGC釋放的內(nèi)存是不會還給操作系統(tǒng)的尼夺。

而在Java 13中,JEP 351再次對ZGC做了增強,本次 ZGC 可以將未使用的堆內(nèi)存返回給操作系統(tǒng)淤堵。之所以引入這個特性寝衫,是因為如今有很多場景中內(nèi)存是比較昂貴的資源,在以下情況中拐邪,將內(nèi)存還給操作系統(tǒng)還是很有必要的:

1慰毅、那些需要根據(jù)使用量付費的容器

2、應用程序可能長時間處于空閑狀態(tài)并與許多其他應用程序共享或競爭資源的環(huán)境扎阶。

3汹胃、應用程序在執(zhí)行期間可能有非常不同的堆空間需求。例如东臀,啟動期間所需的堆可能大于稍后在穩(wěn)定狀態(tài)執(zhí)行期間所需的堆着饥。

3、Reimplement the Legacy Socket API

使用易于維護和調試的更簡單惰赋、更現(xiàn)代的實現(xiàn)替換 java.net.Socket 和 java.net.ServerSocket API宰掉。

java.net.Socket和java.net.ServerSocket的實現(xiàn)非常古老,這個JEP為它們引入了一個現(xiàn)代的實現(xiàn)×薇簦現(xiàn)代實現(xiàn)是Java 13中的默認實現(xiàn)轨奄,但是舊的實現(xiàn)還沒有刪除,可以通過設置系統(tǒng)屬性jdk.net.usePlainSocketImpl來使用它們流部。

運行一個實例化Socket和ServerSocket的類將顯示這個調試輸出戚绕。這是默認的(新的):

4、Switch Expressions (Preview)

在JDK 12中引入了Switch表達式作為預覽特性枝冀。JEP 354修改了這個特性,它引入了yield語句,用于返回值。這意味著琉苇,switch表達式(返回值)應該使用yield, switch語句(不返回值)應該使用break臊诊。

在以前,我們想要在switch中返回內(nèi)容泵三,還是比較麻煩的,一般語法如下:

inti;

switch(x)?{

case"1":

i=1;

break;

case"2":

i=2;

break;

default:

????????i?=?x.length();

break;

}

在JDK13中使用以下語法:

int?i?=switch(x)?{

case"1"->?1;

case"2"->?2;

default->?{

int?len?=?args[1].length();

yieldlen;

????}

};

或者

inti?=switch(x)?{

case"1":yield1;

case"2":yield2;

default:?{

intlen?=?args[1].length();

yieldlen;

????}

};

在這之后,switch中就多了一個關鍵字用于跳出switch塊了户辱,那就是yield,他用于返回一個值糙臼。和return的區(qū)別在于:return會直接跳出當前循環(huán)或者方法庐镐,而yield只會跳出當前switch塊。

5变逃、Text Blocks (Preview)

在JDK 12中引入了Raw String Literals特性必逆,但在發(fā)布之前就放棄了。這個JEP在引入多行字符串文字(text block)在意義上是類似的。

text block名眉,文本塊粟矿,是一個多行字符串文字,它避免了對大多數(shù)轉義序列的需要损拢,以可預測的方式自動格式化字符串陌粹,并在需要時讓開發(fā)人員控制格式。

我們以前從外部copy一段文本串到Java中福压,會被自動轉義掏秩,如有一段以下字符串:

Hello,?Hollis

將其復制到Java的字符串中,會展示成以下內(nèi)容:

"

"?+

"

"?+

"

Hello,?Hollis

"?+

"

"?+

"

";

即被自動進行了轉義隧膏,這樣的字符串看起來不是很直觀哗讥,在JDK 13中,就可以使用以下語法了:

"""

Hello,?Hollis

"""

;

使用"""作為文本塊的開始符合結束符胞枕,在其中就可以放置多行的字符串杆煞,不需要進行任何轉義「海看起來就十分清爽了决乎。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市派桩,隨后出現(xiàn)的幾起案子构诚,更是在濱河造成了極大的恐慌,老刑警劉巖铆惑,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件范嘱,死亡現(xiàn)場離奇詭異,居然都是意外死亡员魏,警方通過查閱死者的電腦和手機丑蛤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來撕阎,“玉大人受裹,你說我怎么就攤上這事÷彩” “怎么了棉饶?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長镇匀。 經(jīng)常有香客問我照藻,道長,這世上最難降的妖魔是什么坑律? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任岩梳,我火速辦了婚禮囊骤,結果婚禮上,老公的妹妹穿的比我還像新娘冀值。我一直安慰自己也物,他們只是感情好,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布列疗。 她就那樣靜靜地躺著滑蚯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪抵栈。 梳的紋絲不亂的頭發(fā)上告材,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機與錄音古劲,去河邊找鬼斥赋。 笑死,一個胖子當著我的面吹牛产艾,可吹牛的內(nèi)容都是我干的疤剑。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼闷堡,長吁一口氣:“原來是場噩夢啊……” “哼隘膘!你這毒婦竟也來了?” 一聲冷哼從身側響起杠览,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤弯菊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后踱阿,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體管钳,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年软舌,在試婚紗的時候發(fā)現(xiàn)自己被綠了蹋嵌。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡葫隙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出躏仇,到底是詐尸還是另有隱情恋脚,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布焰手,位于F島的核電站糟描,受9級特大地震影響,放射性物質發(fā)生泄漏书妻。R本人自食惡果不足惜船响,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧见间,春花似錦聊闯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至史侣,卻和暖如春拴泌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背惊橱。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工蚪腐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人税朴。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓回季,卻偏偏與公主長得像,于是被迫代替她去往敵國和親掉房。 傳聞我的和親對象是個殘疾皇子茧跋,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345