JAVA8——JAVA成長之路

2020-12-14 JAVA數據結構

毫無疑問,Java 8發行版是自Java 5(發行於2004,已經過了相當一段時間了)以來最具革命性的版本。Java 8 為Java語言、編譯器、類庫、開發工具與JVM(Java虛擬機)帶來了大量新特性。在這篇教程中,我們將一一探索這些變化,並用真實的例子說明它們適用的場景。

這篇教程由以下幾部分組成,它們分別涉及到Java平臺某一特定方面的內容:

Java語言

編譯器

類庫

工具

Java運行時(JVM)

2.Java語言的新特性

不管怎麼說,Java 8都是一個變化巨大的版本。你可能認為Java 8耗費了大量的時間才得以完成是為了實現了每個Java程式設計師所期待的特性。在這個小節裡,我們將會涉及到這些特性的大部分。

2.1 Lambda表達式與Functional接口

Lambda表達式(也稱為閉包)是整個Java 8發行版中最受期待的在Java語言層面上的改變,Lambda允許把函數作為一個方法的參數(函數作為參數傳遞進方法中),或者把代碼看成數據:函數式程式設計師對這一概念非常熟悉。在JVM平臺上的很多語言(Groovy,Scala,……)從一開始就有Lambda,但是Java程式設計師不得不使用毫無新意的匿名類來代替lambda。

關於Lambda設計的討論佔用了大量的時間與社區的努力。可喜的是,最終找到了一個平衡點,使得可以使用一種即簡潔又緊湊的新方式來構造Lambdas。在最簡單的形式中,一個lambda可以由用逗號分隔的參數列表、–>符號與函數體三部分表示。例如:

請注意參數e的類型是由編譯器推測出來的。同時,你也可以通過把參數類型與參數包括在括號中的形式直接給出參數的類型:

在某些情況下lambda的函數體會更加複雜,這時可以把函數體放到在一對花括號中,就像在Java中定義普通函數一樣。例如:

"a""b""d"System.out.print( e );

和:

Lambda可能會返回一個值。返回值的類型也是由編譯器推測出來的。如果lambda的函數體只有一行的話,那麼沒有必要顯式使用return語句。下面兩個代碼片段是等價的:

和:

"a""b""d"int return

public Functional {

method();

}

需要記住的一件事是:默認方法與靜態方法並不影響函數式接口的契約,可以任意使用:

Lambda是Java 8最大的賣點。它具有吸引越來越多程式設計師到Java平臺上的潛力,並且能夠在純Java語言環境中提供一種優雅的方式來支持函數式編程。更多詳情可以參考官方文檔。

2.2 接口的默認方法與靜態方法

Java 8用默認方法與靜態方法這兩個新概念來擴展接口的聲明。默認方法使接口有點像Traits(Scala中特徵(trait)類似於Java中的Interface,但它可以包含實現代碼,也就是目前Java8新增的功能),但與傳統的接口又有些不一樣,它允許在已有的接口中添加新方法,而同時又保持了與舊版本代碼的兼容性。

默認方法與抽象方法不同之處在於抽象方法必須要求實現,但是默認方法則沒有這個要求。相反,每個接口都必須提供一個所謂的默認實現,這樣所有的接口實現者將會默認繼承它(如果有必要的話,可以覆蓋這個默認實現)。讓我們看看下面的例子:

下面的一小段代碼片段把上面的默認方法與靜態方法黏合到一起。

在JVM中,默認方法的實現是非常高效的,並且通過字節碼指令為方法調用提供了支持。默認方法允許繼續使用現有的Java接口,而同時能夠保障正常的編譯過程。這方面好的例子是大量的方法被添加到java.util.Collection接口中去:stream(),parallelStream(),forEach(),removeIf(),……

儘管默認方法非常強大,但是在使用默認方法時我們需要小心注意一個地方:在聲明一個默認方法前,請仔細思考是不是真的有必要使用默認方法,因為默認方法會帶給程序歧義,並且在複雜的繼承體系中容易產生編譯錯誤。更多詳情請參考官方文檔

2.3 方法引用

方法引用提供了非常有用的語法,可以直接引用已有Java類或對象(實例)的方法或構造器。與lambda聯合使用,方法引用可以使語言的構造更緊湊簡潔,減少冗餘代碼。

下面,我們以定義了4個方法的Car這個類作為例子,區分Java中支持的4種不同的方法引用。

第二種方法引用是靜態方法引用,它的語法是Class::static_method。請注意這個方法接受一個Car類型的參數。

第三種方法引用是特定類的任意對象的方法引用,它的語法是Class::method。請注意,這個方法沒有參數。

最後,第四種方法引用是特定對象的方法引用,它的語法是instance::method。請注意,這個方法接受一個Car類型的參數

Car police = Car.create( Car::);

cars.forEach( police::follow );

運行上面的Java程序在控制臺上會有下面的輸出(Car的實例可能不一樣):

正如我們看到的,這裡有個使用@Repeatable( Filters.class )註解的註解類Filter,Filters僅僅是Filter註解的數組,但Java編譯器並不想讓程式設計師意識到Filters的存在。這樣,接口Filterable就擁有了兩次Filter(並沒有提到Filter)註解。

同時,反射相關的API提供了新的函數getAnnotationsByType()來返回重複註解的類型(請注意Filterable.class.getAnnotation( Filters.class )經編譯器處理後將會返回Filters的實例)。

程序輸出結果如下:

null}

T getOrDefault( T value, T defaultValue ) {

( value != ) ? value : defaultValue;

public TypeInference {

staticmain(String[] args) {

Value< String > value = Value<>();

"22"}

}

Value.defaultValue()的參數類型可以被推測出,所以就不必明確給出。在Java 7中,相同的例子將不會通過編譯,正確的書寫方式是 Value.< String >defaultValue()。

2.6 擴展註解的支持

Java 8擴展了註解的上下文。現在幾乎可以為任何東西添加註解:局部變量、泛型類、父類與接口的實現,就連方法的異常也能添加註解。下面演示幾個例子:

com.javacodegeeks.java8.annotations;

java.lang.annotation.ElementType;

import java.lang.annotation.RetentionPolicy;

import java.util.ArrayList;

import

public Annotations {

( RetentionPolicy.RUNTIME )

( { ElementType.TYPE_USE, ElementType.TYPE_PARAMETER } )

@interface}

staticHolder< T > @NonEmptypublic method() @NonEmpty }

@SuppressWarnings"unused" public void final new Holder< String >();

Collection< String > strings = ArrayList<>();

public ParameterNames {

staticmain(String[] args) Exception {

class"main"classforfinal System.out.println( + parameter.getName() );

}

}

如果不使用–parameters參數來編譯這個類,然後運行這個類,會得到下面的輸出:

如果使用–parameters參數來編譯這個類,程序的結構會有所不同(參數的真實名字將會顯示出來):

對於有經驗的Maven用戶,通過maven-compiler-plugin的配置可以將-parameters參數添加到編譯器中去。

plugin<>org.apache.maven.plugins</>

artifactIdartifactId<>3.1</>

configuration<>-parameters</>

sourcesource<>1.8</>

configurationplugin

);

System.out.println( + fullName.isPresent() );

System.out.println( + fullName.orElseGet( () -> ) );

System.out.println( fullName.map( s -> + s + ).orElse( ) );

如果Optional類的實例為非空值的話,isPresent()返回true,否從返回false。為了防止Optional為空值,orElseGet()方法通過回調函數來產生一個默認值。map()函數對當前Optional的值進行轉化,然後返回一個新的Optional實例。orElse()方法和orElseGet()方法類似,但是orElse接受一個默認值而不是一個回調函數。下面是這個程序的輸出:

更多詳情請參考官方文檔

4.2 Stream

最新添加的Stream API(java.util.stream) 把真正的函數式編程風格引入到Java中。這是目前為止對Java類庫最好的補充,因為Stream API可以極大提供Java程式設計師的生產力,讓程式設計師寫出高效率、乾淨、簡潔的代碼。

Stream API極大簡化了集合框架的處理(但它的處理的範圍不僅僅限於集合框架的處理,這點後面我們會看到)。讓我們以一個簡單的Task類為例進行介紹:

classprivate Status {

};

staticclassprivate Status status;

final

finalfinalthisthis}

Integer getPoints() {

points;

public return }

public return "[%s, %d]"}

Task( Status.OPEN, ),

Task( Status.OPEN, ),

Task( Status.CLOSED, )

);

我們下面要討論的第一個問題是所有狀態為OPEN的任務一共有多少分數?在Java 8以前,一般的解決方式用foreach循環,但是在Java 8裡面我們可以使用stream:一串支持連續、並行聚集操作的元素。

long .stream()

.mapToInt( Task::getPoints )

"Total points: "

totalPoints = tasks

.parallel()

// or map( Task::getPoints )

0"Total points (all tasks): "

這個例子的控制臺輸出如下:

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

Collection< String > result = tasks

// Stream< String >

// IntStream

// LongStream

// DoubleStream

// Stream< Double >

long100// LongStream

"%"// Stream< String>

// List< String >

System.out.println( result );

下面是這個例子的控制臺輸出:

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

Path path = File( filename ).toPath();

trylines.onClose( () -> System.out.println() ).forEach( System.out::println );

}

對一個stream對象調用onClose方法會返回一個在原有功能基礎上新增了關閉功能的stream對象,當對stream對象調用close()方法時,與關閉相關的處理器就會執行。

Stream API、Lambda表達式與方法引用在接口默認方法與靜態方法的配合下是Java 8對現代軟體開發範式的回應。更多詳情請參考官方文檔。

4.3 Date/Time API (JSR 310)

Java 8通過發布新的Date-Time API (JSR 310)來進一步加強對日期與時間的處理。對日期與時間的操作一直是Java程式設計師最痛苦的地方之一。標準的 java.util.Date以及後來的java.util.Calendar一點沒有改善這種情況(可以這麼說,它們一定程度上更加複雜)。

這種情況直接導致了Joda-Time——一個可替換標準日期/時間處理且功能非常強大的Java API的誕生。Java 8新的Date-Time API (JSR 310)在很大程度上受到Joda-Time的影響,並且吸取了其精髓。新的java.time包涵蓋了所有處理日期,時間,日期/時間,時區,時刻(instants),過程(during)與時鐘(clock)的操作。在設計新版API時,十分注重與舊版API的兼容性:不允許有任何的改變(從java.util.Calendar中得到的深刻教訓)。如果需要修改,會返回這個類的一個新實例。

讓我們用例子來看一下新版API主要類的使用方法。第一個是Clock類,它通過指定一個時區,然後就可以獲取到當前的時刻,日期與時間。Clock可以替換System.currentTimeMillis()與TimeZone.getDefault()。

Clock clock = Clock.systemUTC();

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

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

下面是程序在控制臺上的輸出:

LocalTime time = LocalTime.now();

final

2014-04-12

11:25:54.568

15:25:54.568

LocaleDateTime把LocaleDate與LocaleTime的功能合併起來,它持有的是ISO-8601格式無時區信息的日期與時間。下面是一個快速入門的例子。

LocalDateTime datetime = LocalDateTime.now();

final

2014-04-12T15:37:52.309

如果你需要特定時區的日期/時間,那麼ZonedDateTime是你的選擇。它持有ISO-8601格式具具有時區信息的日期與時間。下面是一些不同時區的例子:

Get the zoned /time

final ZonedDateTime zonedDatetime = ZonedDateTime.now();

final ZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now( clock );

final ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now( ZoneId.of( ) );

System.out.println( zonedDatetime );

System.out.println( zonedDatetimeFromClock );

System.out.println( zonedDatetimeFromZone );

下面是程序在控制臺上的輸出:

最後,讓我們看一下Duration類:在秒與納秒級別上的一段時間。Duration使計算兩個日期間的不同變的十分簡單。下面讓我們看一個這方面的例子。

上面的例子計算了兩個日期2014年4月16號與2014年4月16號之間的過程。下面是程序在控制臺上的輸出:

inin

我們在後面的Java新工具章節會再次談到Nashorn。

4.5 Base64

在Java 8中,Base64編碼已經成為Java類庫的標準。它的使用十分簡單,下面讓我們看一個例子:

com.javacodegeeks.java8.base64;

java.nio.charset.StandardCharsets;

import

public Base64s {

staticmain(String[] args) {

String text = ;

String encoded = Base64

.encodeToString( text.getBytes( StandardCharsets.UTF_8 ) );

final new Base64.getDecoder().decode( encoded ),

System.out.println( decoded );

Base64 finally Java 8!

Base64類同時還提供了對URL、MIME友好的編碼器與解碼器(Base64.getUrlEncoder() / Base64.getUrlDecoder(), Base64.getMimeEncoder() / Base64.getMimeDecoder())。

4.6 並行(parallel)數組

Java 8增加了大量的新方法來對數組進行並行處理。可以說,最重要的是parallelSort()方法,因為它可以在多核機器上極大提高數組排序的速度。下面的例子展示了新方法(parallelXxx)的使用。

上面的代碼片段使用了parallelSetAll()方法來對一個有20000個元素的數組進行隨機賦值。然後,調用parallelSort方法。這個程序首先列印出前10個元素的值,之後對整個數組排序。這個程序在控制臺上的輸出如下(請注意數組元素是隨機生產的):1

1;

};

-> org.apache.commons.logging not found

-> org.springframework.asm.commons not found

-> java.lang

-> java.lang.reflect

<code plain"="" style="border: 0px !important; font-family: Consolas, "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important; border-radius: 0px !important; bottom: auto !important; float: none !important; height: auto !important; left: auto !important; line-height: 1.1em !important; outline: 0px !important; overflow: visible !important; position: static !important; right: auto !important; top: auto !important; vertical-align: baseline !important; width: auto !important; box-sizing: content-box !important; min-height: auto !important; background: none !important;">-> java.util

更多詳情請參考官方文檔

6. Java虛擬機(JVM)的新特性

PermGen空間被移除了,取而代之的是Metaspace(JEP 122)。JVM選項-XX:PermSize-XX:MaxPermSize分別被-XX:MetaSpaceSize-XX:MaxMetaspaceSize所代替。

相關焦點

  • 終於搞懂了java8的內存結構,再也不糾結方法區和常量池了
    java8內存結構介紹  java虛擬機在jdk8改變了許多,網絡上各種解釋都有,在查閱了官方文檔以及一下大佬的解釋以後,我來粗淺的介紹一下我理解的java8的內存結構。java8內存結構圖
  • 俠說java8-LocalDateTime等時間使用手冊(全),先mark後看
    以上是從普通的Date對象轉換成java8時間的操作步驟,需要特別注意下時區的問題。而在java8之前,我們是使用 after(), before(),equals。方法名有所區別,但是功能大同小異。4.java8中的瞬點InstantInstant代表一個瞬時的時間點值對象。
  • Python和Java語言,新手應該先學哪個
    我先表明下自己的觀點,java和python這兩門語言,都要學習,但是,要有先後順序。強烈推薦先學習java。java是個嚴謹、高效的程式語言,出道20餘年,在編程兵器排行榜上,常年佔據前三。從這就能看出java這門語言深得人心,是見過世面的程式語言,這麼多年,什麼風雨都經歷過,從網際網路草莽時代,憑藉一己之力,為網際網路推向全宇宙做出了不可磨滅的貢獻,到了移動網際網路時代,Android的興起,讓java又活出了第二個生命,羨煞旁人。再到大數據時代,java又毫無懸念的霸屏了,Hadoop的技術圈裡,java始終是焦點。以上就足以讓你選擇java了。
  • mysql進行查詢時可以用limit限制查詢結果的條數,Java可以嗎?
    那在java中有沒有這樣的功能呢?有的朋友會說,你既然這麼問,那肯定是有了。還有的看過我最近幾篇文章(《java中有沒有類似sql的group by的功能呢》、《java8流操作之map映射》、《給Java8的流加個篩子》)的朋友也許會說,該不會還是java8中流式編程中提供的功能吧。
  • 巧用Java8中的Stream,讓集合操作飛起來!
    本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫作者:堅持就是勝利juejin.im/post/5d5e2616f265da03b638b28a簡介
  • 0基礎學java有多難,到底難在哪裡
    java作為目前市場應用廣泛的一門成熟的程式語言,市場需求大,且可選擇的崗位種類比較多,也正是因為這些需求,讓越來越多的人選擇學習java知識,成為java程式設計師,來獲取更好的薪資。但是java語言相對於來說也是比較難學的,尤其是對從來沒接觸過過程式語言的零基礎小夥伴來說,可以說是學習之路會更加困難,那麼0基礎學習java到底有多難,難在哪裡呢?你知道嗎?
  • 動力節點Java學院2021年Java學習路線圖最新出爐啦
    ://www.bjpowernode.com/javavideo/110.htmlJavase進階http://www.bjpowernode.com/javavideo/144.html資料庫MySQL視頻http://www.bjpowernode.com/javavideo/111.htmlJDBC視頻http
  • 跟我學java編程—Java布爾類型
    java語言通過關鍵字boolean來定義布爾類型變量,只有true和false兩個值,分別代表布爾邏輯中的「真」和「假」。Java語言雖然定義了boolean這種數據類型,但是只對它提供了非常有限的支持。在Java虛擬機中沒有任何供boolean值專用的字節碼指令,Java語言表達式所操作的boolean值,在編譯之後都使用Java虛擬機中的int數據類型來代替。
  • LinkedList——JAVA成長之路
  • java是什麼文件格式?.java文件怎麼打開?
    java是什麼文件?  Java文件是由Sun Microsystems公司於1995年5月推出的Java程序設計語言和Java平臺的總稱。用Java實現的HotJava瀏覽器(支持Java applet)顯示了Java的魅力:跨平臺、動態的Web、Internet計算。從此,Java被廣泛接受並推動了Web的迅速發展,常用的瀏覽器現在均支持Java applet。
  • java中的try是什麼?|java的異常處理類型
    1、try-是java異常處理類型中的一個。那麼,異常處理又是啥?2、異常-說的就是編寫程序時出現的錯誤。2.1:java提供異常處理類Throwable2.2:Throwable類分為Error和Exception兩個子類2.3:Error由java本身來處理2.4:我們要捕獲和拋出的異常是Exception這類的異常
  • JAVA8 新特性詳解
    System.out.println("DefalutTest static 方法");}}public class Main {public static void main(String[] args) {//匿名內部類--java8
  • 學習java難不難,零基礎可以學習嗎?
    學習java難不難,零基礎可以學習嗎? 2020年07月18日 12:35作者:黃頁編輯:黃頁 學習java其實並不是很困難,
  • 跟我學java編程—Java嵌套if
    在D盤Java目錄下,新建「EmbedSample.java」文件。用記事本打開「EmbedSample.java」文件,輸入以下代碼:代碼結構分析程序功能主要是演示嵌套if結構的使用。編譯「EmbedSample.java」文件,在命令行窗口輸入「javac EmbedSample.java」並執行命令,編譯通過後,在命令行窗口輸入「java EmbedSample」運行Java程序,命令行窗口顯示如下信息:
  • Go+iris吊打Java+SpringBoot,是Java老了嗎?且慢,Vert.x有話說
    Go+Iris vs Java+SpringBoot 為您揭曉答案那個結果很容易給人一個錯覺:是java老了嗎?當然不是!Java界的性能擔當根本就不是Spring,只是因為國內用java的web框架主要是Spring,我們才拿它來測……本次我們就請出java界的性能擔當——Vert.x,跟Iris再比一次這次的結果會比較有說服力因為iris在官網自稱自己是
  • 我的世界:java版與基巖版,相差兩歲的哥弟倆,有多少不同特性?
    2009年,notch著手開發Minecraft的java版本。2011年,mojang工作室開發了Minecraft移動版,又稱攜帶版,最後被統稱為基巖版。從開發時間來講,java版與基巖版算是一對相差2歲的兄弟倆。由於版本不同,帶來了很多遊戲特性的差異。
  • 計算機二級java好考嗎?
    今天就來說一下計算二級中的java這一科目。小編通過了19年9月的二級java考試,下面來為大家分享下經驗。要有編程基礎或者編程興趣報考計算機二級java科目的同學,我想應該對java這門語言有一定的了解或者是計算機相關專業的,有一定的編程基礎,或者對編程感興趣,不然也不會報名二級java。
  • 虛擬主機 支持java嗎
    虛擬主機 支持java嗎?虛擬主機支持java的,但java需要專屬環境、硬體支持,不像其他語言,如ASP、PHP等,很多情況下,可以共享環境、硬體等。所以,提供java環境的虛擬主機比較少,只有少部分服務商,比如西部數碼。
  • 跟我學java編程—Java邏輯運算符
    示例1:邏輯運算符在D盤Java目錄下,新建「LogicSample.java」文件。用記事本打開「LogicSample.java」文件,輸入以下代碼:代碼結構分析程序功能主要是演示邏輯運算符的用法。類似語句「bJudge = !