[轉載] Java9發布回顧Java 8的十大新特性

參考鏈接: Java中的DoubleStream mapToObj()

java9已經在北京時間9月22日正式發布,開發者可以在oracle jdk官網上下載到最新的jdk9。?

今天,我們先來一起復習一下2014年發布的Java 8的十大新特性。先來喝杯java~~~?

??

按照java升級的傳統,偶數版的(468)改動較小,奇數版的(579)都是大改動。但對于java8而言是一次變化巨大的更新,耗費了工程師大量的時間,還借鑒了很多其它語言和類庫。這里為大家列舉十個新特性。? ?

??

?

?

?Lambda表達式??

?Lambda表達式(也稱為閉包)是整個Java 8發行版中最受期待的在Java語言層面上的改變,Lambda允許把函數作為一個方法的參數(函數作為參數傳遞進方法中),或者把代碼看成數據:函數式程序員對這一概念非常熟悉。在JVM平臺上的很多語言(Groovy,Scala,……)從一開始就有Lambda,但是Java程序員不得不使用毫無新意的匿名類來代替lambda。? 關于Lambda設計的討論占用了大量的時間與社區的努力。可喜的是,最終找到了一個平衡點,使得可以使用一種即簡潔又緊湊的新方式來構造Lambdas。在最簡單的形式中,一個lambda可以由用逗號分隔的參數列表、–>符號與函數體三部分表示。例如:??

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

?

?

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

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

?

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

Arrays.asList( "a", "b", "d" ).forEach( e -> {

? ? System.out.print( e );

? ? System.out.print( e );

} );

?Lambda可以引用類的成員變量與局部變量(如果這些變量不是final的話,它們會被隱含的轉為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可能會返回一個值。返回值的類型也是由編譯器推測出來的。如果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;

} );

?

語言設計者投入了大量精力來思考如何使現有的函數友好地支持lambda。最終采取的方法是:增加函數式接口的概念。函數式接口就是一個具有一個方法的普通接口。像這樣的接口,可以被隱式轉換為lambda表達式。java.lang.Runnable與java.util.concurrent.Callable是函數式接口最典型的兩個例子。在實際使用過程中,函數式接口是容易出錯的:如有某個人在接口定義中增加了另一個方法,這時,這個接口就不再是函數式的了,并且編譯過程也會失敗。為了克服函數式接口的這種脆弱性并且能夠明確聲明接口作為函數式接口的意圖,Java 8增加了一種特殊的注解@FunctionalInterface(Java 8中所有類庫的已有接口都添加了@FunctionalInterface注解)。讓我們看一下這種函數式接口的定義:

?

@FunctionalInterface

public interface Functional {

? ? void method();

}

?

需要記住的一件事是:

默認方法與靜態方法

并不影響函數式接口的契約,可以任意使用:

?

@FunctionalInterface

public interface FunctionalDefaultMethods {

? ? void method();

? ? ? ? ?

? ? default void defaultMethod() {? ? ? ? ? ??

? ? }? ? ? ??

}

?

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

??

??

?

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

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

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

private interface Defaulable {

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

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

? ? default String notRequired() {?

? ? ? ? return "Default implementation";?

? ? }? ? ? ??

}

? ? ? ? ?

private static class DefaultableImpl implements Defaulable {

}

? ? ?

private static class OverridableImpl implements Defaulable {

? ? @Override

? ? public String notRequired() {

? ? ? ? return "Overridden implementation";

? ? }

}

?

?

?Defaulable接口用關鍵字default聲明了一個默認方法notRequired(),Defaulable接口的實現者之一DefaultableImpl實現了這個接口,并且讓默認方法保持原樣。Defaulable接口的另一個實現者OverridableImpl用自己的方法覆蓋了默認方法。?

?Java 8帶來的另一個有趣的特性是接口可以聲明(并且可以提供實現)靜態方法。例如:??

private interface DefaulableFactory {

? ? // Interfaces now allow static methods

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

? ? ? ? return supplier.get();

? ? }

}

?

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

public static void main( String[] args ) {

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

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

? ? ? ? ?

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

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

}

這個程序的控制臺輸出如下:

?

Default implementation

Overridden implementation

?

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

?

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

官方文檔

?

?

?

?

?Stream??

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

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

public class Streams? {

? ? private enum Status {

? ? ? ? OPEN, CLOSED

? ? };

? ? ?

? ? private static final class Task {

? ? ? ? private final Status status;

? ? ? ? private final Integer points;

?

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

? ? ? ? ? ? this.status = status;

? ? ? ? ? ? this.points = points;

? ? ? ? }

? ? ? ? ?

? ? ? ? public Integer getPoints() {

? ? ? ? ? ? return points;

? ? ? ? }

? ? ? ? ?

? ? ? ? public Status getStatus() {

? ? ? ? ? ? return status;

? ? ? ? }

? ? ? ? ?

? ? ? ? @Override

? ? ? ? public String toString() {

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

? ? ? ? }

? ? }

}

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

final Collection< Task > tasks = Arrays.asList(

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

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

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

);

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

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

final long totalPointsOfOpenTasks = tasks

? ? .stream()

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

? ? .mapToInt( Task::getPoints )

? ? .sum();

? ? ? ? ?

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

程序在控制臺上的輸出如下:?

Total points: 18?

?

?這里有幾個注意事項。第一,task集合被轉換化為其相應的stream表示。然后,filter操作過濾掉狀態為CLOSED的task。下一步,mapToInt操作通過Task::getPoints這種方式調用每個task實例的getPoints方法把Task的stream轉化為Integer的stream。最后,用sum函數把所有的分數加起來,得到最終的結果。?

?在繼續講解下面的例子之前,關于stream有一些需要注意的地方(詳情在這里).stream操作被分成了中間操作與最終操作這兩種。?

?中間操作返回一個新的stream對象。中間操作總是采用惰性求值方式,運行一個像filter這樣的中間操作實際上沒有進行任何過濾,相反它在遍歷元素時會產生了一個新的stream對象,這個新的stream對象包含原始stream 中符合給定謂詞的所有元素。?

?像forEach、sum這樣的最終操作可能直接遍歷stream,產生一個結果或副作用。當最終操作執行結束之后,stream管道被認為已經被消耗了,沒有可能再被使用了。在大多數情況下,最終操作都是采用及早求值方式,及早完成底層數據源的遍歷。?

?stream另一個有價值的地方是能夠原生支持并行處理。讓我們來看看這個算task分數和的例子。??

// Calculate total points of all tasks

final double totalPoints = tasks

? ?.stream()

? ?.parallel()

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

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

? ? ?

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

這個例子和第一個例子很相似,但這個例子的不同之處在于這個程序是并行運行的,其次使用reduce方法來算最終的結果。

?

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

Total points (all tasks): 26.0

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

// Group tasks by their status

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

? ? .stream()

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

System.out.println( map );

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

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

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

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

final Collection< String > result = tasks

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

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

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

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

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

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

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

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

? ? ? ? ?

System.out.println( result );

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

[19%, 50%, 30%]

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

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

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

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

}?

?

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

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

? ?

?

?

?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()。??

// Get the system clock as UTC offset?

final Clock clock = Clock.systemUTC();

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

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

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

?

2014-04-12T15:19:29.282Z

1397315969360

我們需要關注的其他類是LocaleDate與LocalTime。LocaleDate只持有ISO-8601格式且無時區信息的日期部分。相應的,LocaleTime只持有ISO-8601格式且無時區信息的時間部分。LocaleDate與LocalTime都可以從Clock中得到。

?

// Get the local date and local time

final LocalDate date = LocalDate.now();

final LocalDate dateFromClock = LocalDate.now( clock );

? ? ? ? ?

System.out.println( date );

System.out.println( dateFromClock );

? ? ? ? ?

// Get the local date and local time

final LocalTime time = LocalTime.now();

final LocalTime timeFromClock = LocalTime.now( clock );

? ? ? ? ?

System.out.println( time );

System.out.println( timeFromClock );

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

?

2014-04-12

2014-04-12

11:25:54.568

15:25:54.568

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

快速入門

的例子。

?

// Get the local date/time

final LocalDateTime datetime = LocalDateTime.now();

final LocalDateTime datetimeFromClock = LocalDateTime.now( clock );

? ? ? ? ?

System.out.println( datetime );

System.out.println( datetimeFromClock );

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

?

2014-04-12T11:37:52.309

2014-04-12T15:37:52.309

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

?

// Get the zoned date/time

final ZonedDateTime zonedDatetime = ZonedDateTime.now();

final ZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now( clock );

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

? ? ? ? ?

System.out.println( zonedDatetime );

System.out.println( zonedDatetimeFromClock );

System.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使計算兩個日期間的不同變的十分簡單。下面讓我們看一個這方面的例子。

?

// Get duration between two dates

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

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

?

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

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

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

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

?

Duration in days: 365

Duration in hours: 8783?

對Java 8在日期/時間API的改進整體印象是非常非常好的。一部分原因是因為它建立在“久戰殺場”的Joda-Time基礎上,另一方面是因為用來大量的時間來設計它,并且這次程序員的聲音得到了認可。更多詳情請參考官方文檔。?

??

?

?

?擴展注解的支持??

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

package com.javacodegeeks.java8.annotations;

?

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

import java.util.ArrayList;

import java.util.Collection;

?

public class Annotations {

? ? @Retention( RetentionPolicy.RUNTIME )

? ? @Target( { ElementType.TYPE_USE, ElementType.TYPE_PARAMETER } )

? ? public @interface NonEmpty {? ? ? ??

? ? }

? ? ? ? ?

? ? public static class Holder< @NonEmpty T > extends @NonEmpty Object {

? ? ? ? public void method() throws @NonEmpty Exception {? ? ? ? ? ?

? ? ? ? }

? ? }

? ? ? ? ?

? ? @SuppressWarnings( "unused" )

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

? ? ? ? final Holder< String > holder = new @NonEmpty Holder< String >();? ? ? ?

? ? ? ? @NonEmpty Collection< @NonEmpty String > strings = new ArrayList<>();? ? ? ?

? ? }

}

?

ElementType.TYPE_USE和ElementType.TYPE_PARAMETER是兩個新添加的用于描述適當的注解上下文的元素類型。在Java語言中,注解處理API也有小的改動來識別新增的類型注解。?

??

??

?

?Optional??

?到目前為止,臭名昭著的空指針異常是導致Java應用程序失敗的最常見原因。以前,為了解決空指針異常,Google公司著名的Guava項目引入了Optional類,Guava通過使用檢查空值的方式來防止代碼污染,它鼓勵程序員寫更干凈的代碼。受到Google Guava的啟發,Optional類已經成為Java 8類庫的一部分。?

?Optional實際上是個容器:它可以保存類型T的值,或者僅僅保存null。Optional提供很多有用的方法,這樣我們就不用顯式進行空值檢測。更多詳情請參考官方文檔。?

?我們下面用兩個小例子來演示如何使用Optional類:一個允許為空值,一個不允許為空值。??

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

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

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

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

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

Full Name is set? false

Full Name: [none]

Hey Stranger!

讓我們來看看另一個例子:

?

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

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

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

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

System.out.println();

?

下面是程序的輸出:?

First Name is set? true

First Name: Tom

Hey Tom!

?

更多詳情請參考官方文檔?

??

??

?

?方法引用??

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

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

public static class Car {

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

? ? ? ? return supplier.get();

? ? }? ? ? ? ? ? ??

? ? ? ? ?

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

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

? ? }

? ? ? ? ?

? ? public void follow( final Car another ) {

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

? ? }

? ? ? ? ?

? ? public void repair() {? ?

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

? ? }

}

第一種方法引用是構造器引用,它的語法是Class::new,或者更一般的Class< T >::new。請注意構造器沒有參數。?

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

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

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

cars.forEach( Car::collide );

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

cars.forEach( Car::repair );

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

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

cars.forEach( police::follow );

運行上面的Java程序在控制臺上會有下面的輸出(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

?

關于方法引用的更多詳情請參考官方文檔。?

??

??

?

?重復注解??

?自從Java 5引入了注解機制,這一特性就變得非常流行并且廣為使用。然而,使用注解的一個限制是相同的注解在同一位置只能聲明一次,不能聲明多次。Java 8打破了這條規則,引入了重復注解機制,這樣相同的注解可以在同一地方聲明多次。?

?重復注解機制本身必須用@Repeatable注解。事實上,這并不是語言層面上的改變,更多的是編譯器的技巧,底層的原理保持不變。讓我們看一個快速入門的例子:??

package com.javacodegeeks.java8.repeatable.annotations;

?

import java.lang.annotation.ElementType;

import java.lang.annotation.Repeatable;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

?

public class RepeatingAnnotations {

? ? @Target( ElementType.TYPE )

? ? @Retention( RetentionPolicy.RUNTIME )

? ? public @interface Filters {

? ? ? ? Filter[] value();

? ? }

? ? ?

? ? @Target( ElementType.TYPE )

? ? @Retention( RetentionPolicy.RUNTIME )

? ? @Repeatable( Filters.class )

? ? public @interface Filter {

? ? ? ? String value();

? ? };

? ? ?

? ? @Filter( "filter1" )

? ? @Filter( "filter2" )

? ? public interface Filterable {? ? ? ??

? ? }

? ? ?

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

? ? ? ? for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) {

? ? ? ? ? ? System.out.println( filter.value() );

? ? ? ? }

? ? }

}

?

?

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

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

?程序輸出結果如下:??

filter1

filter2

?

更多詳情請參考官方文檔?

??

??

?

?JavaScript引擎Nashorn??

?

?Nashorn,一個新的JavaScript引擎隨著Java 8一起公諸于世,它允許在JVM上開發運行某些JavaScript應用。Nashorn就是javax.script.ScriptEngine的另一種實現,并且它們倆遵循相同的規則,允許Java與JavaScript相互調用。下面看一個例子:??

ScriptEngineManager manager = new ScriptEngineManager();

ScriptEngine engine = manager.getEngineByName( "JavaScript" );

? ? ? ? ?

System.out.println( engine.getClass().getName() );

System.out.println( "Result:" + engine.eval( "function f() { return 1; }; f() + 1;" ) );?

?

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

jdk.nashorn.api.scripting.NashornScriptEngine

Result: 2?

??

??

?

?Base64??

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

package com.javacodegeeks.java8.base64;

?

import java.nio.charset.StandardCharsets;

import java.util.Base64;

?

public class Base64s {

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

? ? ? ? final String text = "Base64 finally in Java 8!";

? ? ? ? ?

? ? ? ? final String encoded = Base64

? ? ? ? ? ? .getEncoder()

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

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

? ? ? ? ?

? ? ? ? final String decoded = new String(?

? ? ? ? ? ? Base64.getDecoder().decode( encoded ),

? ? ? ? ? ? StandardCharsets.UTF_8 );

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

? ? }

}

程序在控制臺上輸出了編碼后的字符與解碼后的字符:

?

QmFzZTY0IGZpbmFsbHkgaW4gSmF2YSA4IQ==

Base64 finally in Java 8!?

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

??

?

?

?結束語:??

本文寫于java9發布之后的幾天內,之所以想寫這篇文章是因為自己確實還沒有掌握清楚java 8的這些新特性,在編程語言呈現爆發的時段,java的份額越來越被擠壓的當今,java 8的發布我認為這意味著這是java的一個轉折點,意味著java語言再像其他語言學習,借鑒,這是java語言剛開始的那些年很像。不學習就要被淘汰,人如此,語言亦如此。

?

?

更多更全的java 8新特性請訪問?

Java 8新特性終極指南

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/540240.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/540240.shtml
英文地址,請注明出處:http://en.pswp.cn/news/540240.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

窗體間傳遞數據

前言 做項目的時候&#xff0c;winfrom因為沒有B/S的緩存機制&#xff0c;窗體間傳遞數據沒有B/S頁面傳遞數據那么方便&#xff0c;今天我們就說下winfrom中窗體傳值的幾種方式。 共有字段傳遞 共有字段傳遞實現起來很方便&#xff0c;就是在窗體類中加個共有字段屬性&#xff…

[轉載] c語言中檢查命令行參數_C中的命令行參數

參考鏈接&#xff1a; Java中的命令行參數 c語言中檢查命令行參數 Command line argument is a parameter supplied to the program when it is invoked. Command line argument is an important concept in C programming. It is mostly used when you need to control your …

MySQL關閉Enterprise Server源碼

今天從MySQL官方網站上獲知&#xff0c;MySQL宣布關閉Enterprise Server的源碼&#xff0c;對于廣大開源愛好者來說&#xff0c;這是一個沉重的打擊。雖然免費的用戶群體一般僅僅使用MySQL Community Server&#xff08;開源免費社區版&#xff09;&#xff0c;但關閉MySQL Ent…

[轉載] Java中Scanner用法總結

參考鏈接&#xff1a; Java之Scanner類 最近在做OJ類問題的時候&#xff0c;經常由于Scanner的使用造成一些細節問題導致程序不通過&#xff08;最慘的就是網易筆試&#xff0c;由于sc死循環了也沒發現&#xff0c;導致AC代碼也不能通過。。。&#xff09;&#xff0c;因此對S…

os和shutil模塊

import os //os模塊基本實現了linux系統中所有的命令 os.system(終端命令)&#xff1a;在終端執行命令 os.getcwd():獲取當前的工作路徑 os.chdir():修改工作路徑 os.chmod():修改權限 os.chown():修改屬主屬組 os.mkdir():創建單個目錄&#xff0c;當目錄存在時報異常&…

[轉載] JAVA語言程序設計(基礎篇)第十版課后題答案(第一章)

參考鏈接&#xff1a; Java中的Scanner和nextChar() JAVA語言程序設計&#xff08;基礎篇&#xff09;第十版課后題答案 第一章 第二題 /** Created by ysy on 2018/7/6. */ public class text2 { public static void main(String[] args){ for(int i 0; i < 5; i) Syste…

java.util.Date和java.sql.Date 一點區別

最近無意中發現&#xff0c;在oracle中同一樣的一個Date類型字段&#xff0c;存儲的日期格式有兩種不同的情況&#xff0c;第一種是2011-1-1 12:00:00&#xff0c;第二種是2011-1-1&#xff0c;仔細查找發現在向數據庫中寫數據的時候定義的變量的問題。 第一種是&#xff1a;ja…

[轉載] java中關于用\t格式輸出

參考鏈接&#xff1a; 用Java格式化輸出 看了好多人關于\t的用法&#xff0c;感覺找不到自己想要的答案&#xff0c;所以索性就自己輸出來看看&#xff0c;如圖&#xff1a;這樣可以一目了然的看出來&#xff0c;\t&#xff08;制表符&#xff09;的作用就是看前面輸出滿不滿8…

微信搶房軟件開發

2019獨角獸企業重金招聘Python工程師標準>>> 這兩年樓市真可謂是一個"火“字難以形容 經歷了長沙兩次開盤&#xff0c;都沒有搶到&#xff0c;目前還沒有買到房子&#xff0c;說說我的悲劇吧&#xff0c;讓大伙都開心開心 第一次搶房是今年4月份長沙萬科金域國…

[轉載] Java——數組習題

參考鏈接&#xff1a; Java從控制臺讀取輸入的方法 package chap02; import java.util.Scanner; /** * * author admin * date 2020-4-8 * description: * 題目內容&#xff1a; 編寫程序&#xff0c; 從控制臺讀取下面的信息&#xff0c; 每月按22天工作日計算&#xff0c;…

超全Linux備份工具集合,滿足你的所有需要!

經常備份計算機上的數據是個好的做法&#xff0c;它可以手動完成&#xff0c;也可以設置成自動執行。許多備份工具擁有不同的功能特性&#xff0c;讓用戶可以配置備份類型、備份時間、備份對象、將備份活動記入日志及執行更多操作。 1.Rsync這是一款在Linux用戶當中頗受歡迎的命…

[轉載] Java內存管理-你真的理解Java中的數據類型嗎(十)

參考鏈接&#xff1a; Java中的字符串類String 1 做一個積極的人 編碼、改bug、提升自己 我有一個樂園&#xff0c;面向編程&#xff0c;春暖花開&#xff01; 推薦閱讀 第一季 0、Java的線程安全、單例模式、JVM內存結構等知識梳理 1、Java內存管理-程序運行過程&#x…

Linux系統安全加固腳本

閑來無事&#xff0c;整理一個系統安全加固腳本&#xff0c;每個公司的要求不一樣&#xff0c;所以僅供參考&#xff1a; #!/bin/sh echo "00 */1 * * * /usr/sbin/ntpdate 192.168.1.1 >>/var/log/ntpdate.log" > mycrontab crontab mycrontab rm -rf my…

[轉載] 整理下java中stringBuilder和stringBuffer兩個類的區別

參考鏈接&#xff1a; Java中的StringBuffer類 StringBuilder和StringBuffer這兩個類在動態拼接字符串時常用&#xff0c;肯定比String的效率和開銷小&#xff0c;這是因為String的對象不會回收哦。 其實我一直用StringBuilder這個類&#xff0c;因為可以簡寫為sb的變量在程序…

11.13 模10計數器設計

.新建一個工程 Family&#xff1a;FLEX10K Available device&#xff1a;EPF10K20TC144-3 2.設置lpm_counter宏單元參數并連接引腳 連接引腳的時候要注意的是&#xff0c;向量線的連接。 3.時序仿真 檢查無誤后進行下一步 4.載入7448并進行引腳連接 5.分配管腳 再次編譯&#x…

[轉載] java對象在內存中的結構

參考鏈接&#xff1a; 了解Java中的類和對象 今天看到一個不錯的PPT&#xff1a;Build Memory-efficient Java Applications&#xff0c;開篇便提出了一個問題&#xff0c;在Hotspot JVM中&#xff0c;32位機器下&#xff0c;Integer對象的大小是int的幾倍&#xff1f; 我們…

使用valueOf前必須進行校驗

每個枚舉都是java.lang.Enum的子類,都可以訪問Enum類提供的方法,比如hashCode(),name(),valueOf()等..... 其中valueOf()方法會把一個String類型的名稱轉變為枚舉項,也就是枚舉項中查找出字面值與該參數相等的枚舉項,雖然這個方法很簡單,但是JDK卻做了一個對于開發人員來說并不…

[轉載] 【Java】Java基礎知識及其擴展筆記(8千字)

參考鏈接&#xff1a; Java中的StringBuilder類及其示例 Java基礎知識及其擴展筆記 零 l 寫在前面一 l JVM1、【1.1.2.1】java程序運行的一般流程2、【1.1.2.1】JVM一般運行流程3、【1.1.2.1】JIT&#xff08;just in time 即時編譯編譯器&#xff09;4、堆與棧 二 l Java …

多IDC GSLB的部署

之前已經介紹過GSLB的實現原理&#xff0c;這里再向大家講述一下GSLB經常遇到的部署方式&#xff0c;多IDC的部署。很多大型的企業或業務容災要求非常高的客戶都會部署有多個異地的數據中心&#xff0c;以保證其業務的“全天候”不間斷的正常運行&#xff0c;而要整合多個IDC的…

[轉載] Controller報錯:java.lang.NoSuchMethodException: java.util.List.<init>()

參考鏈接&#xff1a; Java8中的java.util.StringJoiner 報錯詳情&#xff1a; java.lang.NoSuchMethodException: java.util.List.<init>() 以及 No primary or default constructor found for interface java.util.List 示例&#xff1a; /** * 此接口會產生以…