自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java并沒沒落:最新Java 8簡明教程譯文

開發(fā) 后端
歡迎閱讀我編寫的Java 8介紹。本教程將帶領(lǐng)你一步一步地認(rèn)識這門語言的新特性。通過簡單明了的代碼示例,你將會學(xué)習(xí)到如何使用默認(rèn)接口方法,Lambda表達式,方法引用和重復(fù)注解??赐赀@篇教程后,你還將對最新推出的API有一定的了解。

“Java并沒有沒落,人們很快就會發(fā)現(xiàn)這一點”

歡迎閱讀我編寫的Java 8介紹。本教程將帶領(lǐng)你一步一步地認(rèn)識這門語言的新特性。通過簡單明了的代碼示例,你將會學(xué)習(xí)到如何使用默認(rèn)接口方法,Lambda表達式,方法引用和重復(fù)注解??赐赀@篇教程后,你還將對最新推出的API有一定的了解,例如:流控制,函數(shù)式接口,map擴展和新的時間日期API等等。

允許在接口中有默認(rèn)方法實現(xiàn)

Java 8 允許我們使用default關(guān)鍵字,為接口聲明添加非抽象的方法實現(xiàn)。這個特性又被稱為擴展方法。下面是我們的第一個例子:

  1. interface Formula {  
  2.     double calculate(int a);  
  3.    
  4.     default double sqrt(int a) {  
  5.         return Math.sqrt(a);  
  6.     }  
  7. }  

在接口Formula中,除了抽象方法caculate以外,還定義了一個默認(rèn)方法sqrt。Formula的實現(xiàn)類只需要實現(xiàn)抽象方法caculate就可以了。默認(rèn)方法sqrt可以直接使用。

  1. Formula formula = new Formula() {  
  2.     @Override 
  3.     public double calculate(int a) {  
  4.         return sqrt(a * 100);  
  5.     }  
  6. };  
  7.    
  8. formula.calculate(100);     // 100.0  
  9. formula.sqrt(16);           // 4.0  

formula對象以匿名對象的形式實現(xiàn)了Formula接口。代碼很啰嗦:用了6行代碼才實現(xiàn)了一個簡單的計算功能:a*100開平方根。我們在下一節(jié)會看到,Java 8 還有一種更加優(yōu)美的方法,能夠?qū)崿F(xiàn)包含單個函數(shù)的對象。

Lambda表達式

讓我們從最簡單的例子開始,來學(xué)習(xí)如何對一個string列表進行排序。我們首先使用Java 8之前的方法來實現(xiàn):

  1. List<String> names = Arrays.asList("peter""anna""mike""xenia");  
  2.    
  3. Collections.sort(names, new Comparator<String>() {  
  4.     @Override 
  5.     public int compare(String a, String b) {  
  6.         return b.compareTo(a);  
  7.     }  
  8. });  

靜態(tài)工具方法Collections.sort接受一個list,和一個Comparator接口作為輸入?yún)?shù),Comparator的實現(xiàn)類可以對輸入的list中的元素進行比較。通常情況下,你可以直接用創(chuàng)建匿名Comparator對象,并把它作為參數(shù)傳遞給sort方法。

除了創(chuàng)建匿名對象以外,Java 8 還提供了一種更簡潔的方式,Lambda表達式。

  1. Collections.sort(names, (String a, String b) -> {  
  2.     return b.compareTo(a);  
  3. });  

你可以看到,這段代碼就比之前的更加簡短和易讀。但是,它還可以更加簡短:

  1. Collections.sort(names, (String a, String b) -> b.compareTo(a));  

只要一行代碼,包含了方法體。你甚至可以連大括號對{}和return關(guān)鍵字都省略不要。不過這還不是最短的寫法:

  1. Collections.sort(names, (a, b) -> b.compareTo(a)); 

Java編譯器能夠自動識別參數(shù)的類型,所以你就可以省略掉類型不寫。讓我們再深入地研究一下lambda表達式的威力吧。

函數(shù)式接口

Lambda表達式如何匹配Java的類型系統(tǒng)?每一個lambda都能夠通過一個特定的接口,與一個給定的類型進行匹配。一個所謂的函數(shù)式接口必須要有且僅有一個抽象方法聲明。每個與之對應(yīng)的lambda表達式必須要與抽象方法的聲明相匹配。由于默認(rèn)方法不是抽象的,因此你可以在你的函數(shù)式接口里任意添加默認(rèn)方法。

任意只包含一個抽象方法的接口,我們都可以用來做成lambda表達式。為了讓你定義的接口滿足要求,你應(yīng)當(dāng)在接口前加上@FunctionalInterface 標(biāo)注。編譯器會注意到這個標(biāo)注,如果你的接口中定義了第二個抽象方法的話,編譯器會拋出異常。

舉例:

  1. @FunctionalInterface 
  2. interface Converter<F, T> {  
  3.     T convert(F from);  
  4. }  
  5.    
  6. Converter<String, Integer> converter = (from) -> Integer.valueOf(from);  
  7. Integer converted = converter.convert("123");  
  8. System.out.println(converted);    // 123  

注意,如果你不寫@FunctionalInterface 標(biāo)注,程序也是正確的。

方法和構(gòu)造函數(shù)引用

上面的代碼實例可以通過靜態(tài)方法引用,使之更加簡潔:

  1. Converter<String, Integer> converter = Integer::valueOf;  
  2. Integer converted = converter.convert("123");  
  3. System.out.println(converted);   // 123  

Java 8 允許你通過::關(guān)鍵字獲取方法或者構(gòu)造函數(shù)的的引用。上面的例子就演示了如何引用一個靜態(tài)方法。而且,我們還可以對一個對象的方法進行引用:

  1. class Something {  
  2.     String startsWith(String s) {  
  3.         return String.valueOf(s.charAt(0));  
  4.     }  
  5. }  
  6.    
  7. Something something = new Something();  
  8. Converter<String, String> converter = something::startsWith;  
  9. String converted = converter.convert("Java");  
  10. System.out.println(converted);    // "J"  

讓我們看看如何使用::關(guān)鍵字引用構(gòu)造函數(shù)。首先我們定義一個示例bean,包含不同的構(gòu)造方法:

  1. class Person {  
  2.     String firstName;  
  3.     String lastName;  
  4.    
  5.     Person() {}  
  6.    
  7.     Person(String firstName, String lastName) {  
  8.         this.firstName = firstName;  
  9.         this.lastName = lastName;  
  10.     }  
  11. }  

接下來,我們定義一個person工廠接口,用來創(chuàng)建新的person對象:

  1. interface PersonFactory<P extends Person> {  
  2.     P create(String firstName, String lastName);  
  3. }  

然后我們通過構(gòu)造函數(shù)引用來把所有東西拼到一起,而不是像以前一樣,通過手動實現(xiàn)一個工廠來這么做。

  1. PersonFactory<Person> personFactory = Person::new;  
  2. Person person = personFactory.create("Peter""Parker");  

我們通過Person::new來創(chuàng)建一個Person類構(gòu)造函數(shù)的引用。Java編譯器會自動地選擇合適的構(gòu)造函數(shù)來匹配PersonFactory.create函數(shù)的簽名,并選擇正確的構(gòu)造函數(shù)形式。

#p#

Lambda的范圍

對于lambdab表達式外部的變量,其訪問權(quán)限的粒度與匿名對象的方式非常類似。你能夠訪問局部對應(yīng)的外部區(qū)域的局部final變量,以及成員變量和靜態(tài)變量。

訪問局部變量

我們可以訪問lambda表達式外部的final局部變量:

  1. final int num = 1;  
  2. Converter<Integer, String> stringConverter =  
  3.         (from) -> String.valueOf(from + num);  
  4.    
  5. stringConverter.convert(2);     // 3  

但是與匿名對象不同的是,變量num并不需要一定是final。下面的代碼依然是合法的:

  1. int num = 1;  
  2. Converter<Integer, String> stringConverter =  
  3.         (from) -> String.valueOf(from + num);  
  4.    
  5. stringConverter.convert(2);     // 3  

然而,num在編譯的時候被隱式地當(dāng)做final變量來處理。下面的代碼就不合法:

  1. int num = 1;  
  2. Converter<Integer, String> stringConverter =  
  3.         (from) -> String.valueOf(from + num);  
  4. num = 3;  

在lambda表達式內(nèi)部企圖改變num的值也是不允許的。

訪問成員變量和靜態(tài)變量

與局部變量不同,我們在lambda表達式的內(nèi)部能獲取到對成員變量或靜態(tài)變量的讀寫權(quán)。這種訪問行為在匿名對象里是非常典型的。

  1. class Lambda4 {  
  2.     static int outerStaticNum;  
  3.     int outerNum;  
  4.    
  5.     void testScopes() {  
  6.         Converter<Integer, String> stringConverter1 = (from) -> {  
  7.             outerNum = 23;  
  8.             return String.valueOf(from);  
  9.         };  
  10.    
  11.         Converter<Integer, String> stringConverter2 = (from) -> {  
  12.             outerStaticNum = 72;  
  13.             return String.valueOf(from);  
  14.         };  
  15.     }  
  16. }  

訪問默認(rèn)接口方法

還記得第一節(jié)里面formula的那個例子么? 接口Formula定義了一個默認(rèn)的方法sqrt,該方法能夠訪問formula所有的對象實例,包括匿名對象。這個對lambda表達式來講則無效。

默認(rèn)方法無法在lambda表達式內(nèi)部被訪問。因此下面的代碼是無法通過編譯的:

  1. Formula formula = (a) -> sqrt( a * 100);  

內(nèi)置函數(shù)式接口

JDK 1.8 API中包含了很多內(nèi)置的函數(shù)式接口。有些是在以前版本的Java中大家耳熟能詳?shù)?,例如Comparator接口,或者Runnable接口。對這些現(xiàn)成的接口進行實現(xiàn),可以通過@FunctionalInterface 標(biāo)注來啟用Lambda功能支持。

此外,Java 8 API 還提供了很多新的函數(shù)式接口,來降低程序員的工作負(fù)擔(dān)。有些新的接口已經(jīng)在Google Guava庫中很有名了。如果你對這些庫很熟的話,你甚至閉上眼睛都能夠想到,這些接口在類庫的實現(xiàn)過程中起了多么大的作用。

Predicates

Predicate是一個布爾類型的函數(shù),該函數(shù)只有一個輸入?yún)?shù)。Predicate接口包含了多種默認(rèn)方法,用于處理復(fù)雜的邏輯動詞(and, or,negate)

  1. Predicate<String> predicate = (s) -> s.length() > 0;  
  2.    
  3. predicate.test("foo");              // true  
  4. predicate.negate().test("foo");     // false  
  5.    
  6. Predicate<Boolean> nonNull = Objects::nonNull;  
  7. Predicate<Boolean> isNull = Objects::isNull;  
  8.    
  9. Predicate<String> isEmpty = String::isEmpty;  
  10. Predicate<String> isNotEmpty = isEmpty.negate(); 

Functions

Function接口接收一個參數(shù),并返回單一的結(jié)果。默認(rèn)方法可以將多個函數(shù)串在一起(compse, andThen)

  1. Function<String, Integer> toInteger = Integer::valueOf;  
  2. Function<String, String> backToString = toInteger.andThen(String::valueOf);  
  3.    
  4. backToString.apply("123");     // "123" 

Suppliers

Supplier接口產(chǎn)生一個給定類型的結(jié)果。與Function不同的是,Supplier沒有輸入?yún)?shù)。

  1. Supplier<Person> personSupplier = Person::new;  
  2. personSupplier.get();   // new Person 

Consumers

Consumer代表了在一個輸入?yún)?shù)上需要進行的操作。

  1. Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);  
  2. greeter.accept(new Person("Luke""Skywalker")); 

Comparators

Comparator接口在早期的Java版本中非常著名。Java 8 為這個接口添加了不同的默認(rèn)方法。

  1. Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);  
  2.    
  3. Person p1 = new Person("John""Doe");  
  4. Person p2 = new Person("Alice""Wonderland");  
  5.    
  6. comparator.compare(p1, p2);             // > 0  
  7. comparator.reversed().compare(p1, p2);  // < 0 

Optionals

Optional不是一個函數(shù)式接口,而是一個精巧的工具接口,用來防止NullPointerEception產(chǎn)生。這個概念在下一節(jié)會顯得很重要,所以我們在這里快速地瀏覽一下Optional的工作原理。

Optional是一個簡單的值容器,這個值可以是null,也可以是non-null??紤]到一個方法可能會返回一個non-null的值,也可能返回一個空值。為了不直接返回null,我們在Java 8中就返回一個Optional.

  1. Optional<String> optional = Optional.of("bam");  
  2.    
  3. optional.isPresent();           // true  
  4. optional.get();                 // "bam"  
  5. optional.orElse("fallback");    // "bam"  
  6.    
  7. optional.ifPresent((s) -> System.out.println(s.charAt(0)));     // "b" 

#p#

Streams

java.util.Stream表示了某一種元素的序列,在這些元素上可以進行各種操作。Stream操作可以是中間操作,也可以是完結(jié)操作。完結(jié)操作會返回一個某種類型的值,而中間操作會返回流對象本身,并且你可以通過多次調(diào)用同一個流操作方法來將操作結(jié)果串起來(就像StringBuffer的append方法一樣————譯者注)。Stream是在一個源的基礎(chǔ)上創(chuàng)建出來的,例如java.util.Collection中的list或者set(map不能作為Stream的源)。Stream操作往往可以通過順序或者并行兩種方式來執(zhí)行。

我們先了解一下序列流。首先,我們通過string類型的list的形式創(chuàng)建示例數(shù)據(jù):

  1. List<String> stringCollection = new ArrayList<>();  
  2. stringCollection.add("ddd2");  
  3. stringCollection.add("aaa2");  
  4. stringCollection.add("bbb1");  
  5. stringCollection.add("aaa1");  
  6. stringCollection.add("bbb3");  
  7. stringCollection.add("ccc");  
  8. stringCollection.add("bbb2");  
  9. stringCollection.add("ddd1"); 

Java 8中的Collections類的功能已經(jīng)有所增強,你可以之直接通過調(diào)用Collections.stream()或者Collection.parallelStream()方法來創(chuàng)建一個流對象。下面的章節(jié)會解釋這個最常用的操作。

Filter

Filter接受一個predicate接口類型的變量,并將所有流對象中的元素進行過濾。該操作是一個中間操作,因此它允許我們在返回結(jié)果的基礎(chǔ)上再進行其他的流操作(forEach)。ForEach接受一個function接口類型的變量,用來執(zhí)行對每一個元素的操作。ForEach是一個中止操作。它不返回流,所以我們不能再調(diào)用其他的流操作。

  1. stringCollection  
  2.     .stream()  
  3.     .filter((s) -> s.startsWith("a"))  
  4.     .forEach(System.out::println);  
  5.    
  6. // "aaa2", "aaa1"  

Sorted

Sorted是一個中間操作,能夠返回一個排過序的流對象的視圖。流對象中的元素會默認(rèn)按照自然順序進行排序,除非你自己指定一個Comparator接口來改變排序規(guī)則。

  1. stringCollection  
  2.     .stream()  
  3.     .sorted()  
  4.     .filter((s) -> s.startsWith("a"))  
  5.     .forEach(System.out::println);  
  6.    
  7. // "aaa1", "aaa2"  

一定要記住,sorted只是創(chuàng)建一個流對象排序的視圖,而不會改變原來集合中元素的順序。原來string集合中的元素順序是沒有改變的。

  1. System.out.println(stringCollection);  
  2. // ddd2, aaa2, bbb1, aaa1, bbb3, ccc, bbb2, ddd1  

Map

map是一個對于流對象的中間操作,通過給定的方法,它能夠把流對象中的每一個元素對應(yīng)到另外一個對象上。下面的例子就演示了如何把每個string都轉(zhuǎn)換成大寫的string. 不但如此,你還可以把每一種對象映射成為其他類型。對于帶泛型結(jié)果的流對象,具體的類型還要由傳遞給map的泛型方法來決定。

  1. stringCollection  
  2.     .stream()  
  3.     .map(String::toUpperCase)  
  4.     .sorted((a, b) -> b.compareTo(a))  
  5.     .forEach(System.out::println);  
  6.    
  7. // "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"  

Match

匹配操作有多種不同的類型,都是用來判斷某一種規(guī)則是否與流對象相互吻合的。所有的匹配操作都是終結(jié)操作,只返回一個boolean類型的結(jié)果。

  1. boolean anyStartsWithA =   
  2.     stringCollection  
  3.         .stream()  
  4.         .anyMatch((s) -> s.startsWith("a"));  
  5.    
  6. System.out.println(anyStartsWithA);      // true  
  7.    
  8. boolean allStartsWithA =   
  9.     stringCollection  
  10.         .stream()  
  11.         .allMatch((s) -> s.startsWith("a"));  
  12.    
  13. System.out.println(allStartsWithA);      // false  
  14.    
  15. boolean noneStartsWithZ =   
  16.     stringCollection  
  17.         .stream()  
  18.         .noneMatch((s) -> s.startsWith("z"));  
  19.    
  20. System.out.println(noneStartsWithZ);      // true  

Count

Count是一個終結(jié)操作,它的作用是返回一個數(shù)值,用來標(biāo)識當(dāng)前流對象中包含的元素數(shù)量。

  1. long startsWithB =   
  2.     stringCollection  
  3.         .stream()  
  4.         .filter((s) -> s.startsWith("b"))  
  5.         .count();  
  6.    
  7. System.out.println(startsWithB);    // 3  

Reduce

該操作是一個終結(jié)操作,它能夠通過某一個方法,對元素進行削減操作。該操作的結(jié)果會放在一個Optional變量里返回。

  1. Optional<String> reduced =  
  2.     stringCollection  
  3.         .stream()  
  4.         .sorted()  
  5.         .reduce((s1, s2) -> s1 + "#" + s2);  
  6.    
  7. reduced.ifPresent(System.out::println);  
  8. // "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"  

#p#

Parallel Streams

像上面所說的,流操作可以是順序的,也可以是并行的。順序操作通過單線程執(zhí)行,而并行操作則通過多線程執(zhí)行。

下面的例子就演示了如何使用并行流進行操作來提高運行效率,代碼非常簡單。

首先我們創(chuàng)建一個大的list,里面的元素都是唯一的:

  1. int max = 1000000;  
  2. List<String> values = new ArrayList<>(max);  
  3. for (int i = 0; i < max; i++) {  
  4.     UUID uuid = UUID.randomUUID();  
  5.     values.add(uuid.toString());  
  6. }  

現(xiàn)在,我們測量一下對這個集合進行排序所使用的時間。

順序排序

  1. long t0 = System.nanoTime();  
  2.    
  3. long count = values.stream().sorted().count();  
  4. System.out.println(count);  
  5.    
  6. long t1 = System.nanoTime();  
  7.    
  8. long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);  
  9. System.out.println(String.format("sequential sort took: %d ms", millis));  
  10.    
  11. // sequential sort took: 899 ms 

并行排序

  1. long t0 = System.nanoTime();  
  2.    
  3. long count = values.parallelStream().sorted().count();  
  4. System.out.println(count);  
  5.    
  6. long t1 = System.nanoTime();  
  7.    
  8. long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);  
  9. System.out.println(String.format("parallel sort took: %d ms", millis));  
  10.    
  11. // parallel sort took: 472 ms  
  12.   

如你所見,所有的代碼段幾乎都相同,唯一的不同就是把stream()改成了parallelStream(), 結(jié)果并行排序快了50%。

Map

正如前面已經(jīng)提到的那樣,map是不支持流操作的。而更新后的map現(xiàn)在則支持多種實用的新方法,來完成常規(guī)的任務(wù)。

  1. Map<Integer, String> map = new HashMap<>();  
  2.    
  3. for (int i = 0; i < 10; i++) {  
  4.     map.putIfAbsent(i, "val" + i);  
  5. }  
  6.    
  7. map.forEach((id, val) -> System.out.println(val));  

上面的代碼風(fēng)格是完全自解釋的:putIfAbsent避免我們將null寫入;forEach接受一個消費者對象,從而將操作實施到每一個map中的值上。

下面的這個例子展示了如何使用函數(shù)來計算map的編碼

  1. map.computeIfPresent(3, (num, val) -> val + num);  
  2. map.get(3);             // val33  
  3.    
  4. map.computeIfPresent(9, (num, val) -> null);  
  5. map.containsKey(9);     // false  
  6.    
  7. map.computeIfAbsent(23, num -> "val" + num);  
  8. map.containsKey(23);    // true  
  9.    
  10. map.computeIfAbsent(3, num -> "bam");  
  11. map.get(3);             // val33 

接下來,我們將學(xué)習(xí),當(dāng)給定一個key值時,如何把一個實例從對應(yīng)的key中移除:

  1. map.remove(3"val3");  
  2. map.get(3);             // val33  
  3.    
  4. map.remove(3"val33");  
  5. map.get(3);             // null  

另一個有用的方法:

  1. map.getOrDefault(42"not found");  // not found  

將map中的實例合并也是非常容易的:

  1. map.merge(9"val9", (value, newValue) -> value.concat(newValue));  
  2. map.get(9);             // val9  
  3.    
  4. map.merge(9"concat", (value, newValue) -> value.concat(newValue));  
  5. map.get(9);             // val9concat 

合并操作先看map中是否沒有特定的key/value存在,如果是,則把key/value存入map,否則merging函數(shù)就會被調(diào)用,對現(xiàn)有的數(shù)值進行修改。

時間日期API

Java 8 包含了全新的時間日期API,這些功能都放在了java.time包下。新的時間日期API是基于Joda-Time庫開發(fā)的,但是也不盡相同。下面的例子就涵蓋了大多數(shù)新的API的重要部分。

Clock

Clock提供了對當(dāng)前時間和日期的訪問功能。Clock是對當(dāng)前時區(qū)敏感的,并可用于替代System.currentTimeMillis()方法來獲取當(dāng)前的毫秒時間。當(dāng)前時間線上的時刻可以用Instance類來表示。Instance也能夠用于創(chuàng)建原先的java.util.Date對象。

  1. Clock clock = Clock.systemDefaultZone();  
  2. long millis = clock.millis();  
  3.    
  4. Instant instant = clock.instant();  
  5. Date legacyDate = Date.from(instant);   // legacy java.util.Date 

Timezones

時區(qū)類可以用一個ZoneId來表示。時區(qū)類的對象可以通過靜態(tài)工廠方法方便地獲取。時區(qū)類還定義了一個偏移量,用來在當(dāng)前時刻或某時間與目標(biāo)時區(qū)時間之間進行轉(zhuǎn)換。

  1. System.out.println(ZoneId.getAvailableZoneIds());  
  2. // prints all available timezone ids  
  3.    
  4. ZoneId zone1 = ZoneId.of("Europe/Berlin");  
  5. ZoneId zone2 = ZoneId.of("Brazil/East");  
  6. System.out.println(zone1.getRules());  
  7. System.out.println(zone2.getRules());  
  8.    
  9. // ZoneRules[currentStandardOffset=+01:00]  
  10. // ZoneRules[currentStandardOffset=-03:00] 

LocalTime

本地時間類表示一個沒有指定時區(qū)的時間,例如,10 p.m.或者17:30:15,下面的例子會用上面的例子定義的時區(qū)創(chuàng)建兩個本地時間對象。然后我們會比較兩個時間,并計算它們之間的小時和分鐘的不同。

  1. LocalTime now1 = LocalTime.now(zone1);  
  2. LocalTime now2 = LocalTime.now(zone2);  
  3.    
  4. System.out.println(now1.isBefore(now2));  // false  
  5.    
  6. long hoursBetween = ChronoUnit.HOURS.between(now1, now2);  
  7. long minutesBetween = ChronoUnit.MINUTES.between(now1, now2);  
  8.    
  9. System.out.println(hoursBetween);       // -3  
  10. System.out.println(minutesBetween);     // -239 

LocalTime是由多個工廠方法組成,其目的是為了簡化對時間對象實例的創(chuàng)建和操作,包括對時間字符串進行解析的操作。

  1. LocalTime late = LocalTime.of(235959);  
  2. System.out.println(late);       // 23:59:59  
  3.    
  4. DateTimeFormatter germanFormatter =  
  5.     DateTimeFormatter  
  6.         .ofLocalizedTime(FormatStyle.SHORT)  
  7.         .withLocale(Locale.GERMAN);  
  8.    
  9. LocalTime leetTime = LocalTime.parse("13:37", germanFormatter);  
  10. System.out.println(leetTime);   // 13:37 

LocalDate

本地時間表示了一個獨一無二的時間,例如:2014-03-11。這個時間是不可變的,與LocalTime是同源的。下面的例子演示了如何通過加減日,月,年等指標(biāo)來計算新的日期。記住,每一次操作都會返回一個新的時間對象。

  1. LocalDate tomorrow = today.plus(1, ChronoUnit.DAYS);  
  2. LocalDate yesterday = tomorrow.minusDays(2);  
  3.    
  4. LocalDate independenceDay = LocalDate.of(2014, Month.JULY, 4);  
  5. DayOfWeek dayOfWeek = independenceDay.getDayOfWeek();  
  6. System.out.println(dayOfWeek);    // FRIDAY<span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 19px;">Parsing a LocalDate from a string is just as simple as parsing a LocalTime:</span> 

解析字符串并形成LocalDate對象,這個操作和解析LocalTime一樣簡單。

  1. DateTimeFormatter germanFormatter =  
  2.     DateTimeFormatter  
  3.         .ofLocalizedDate(FormatStyle.MEDIUM)  
  4.         .withLocale(Locale.GERMAN);  
  5.    
  6. LocalDate xmas = LocalDate.parse("24.12.2014", germanFormatter);  
  7. System.out.println(xmas);   // 2014-12-24 

#p#

LocalDateTime

LocalDateTime表示的是日期-時間。它將剛才介紹的日期對象和時間對象結(jié)合起來,形成了一個對象實例。LocalDateTime是不可變的,與LocalTime和LocalDate的工作原理相同。我們可以通過調(diào)用方法來獲取日期時間對象中特定的數(shù)據(jù)域。

  1. LocalDateTime sylvester = LocalDateTime.of(2014, Month.DECEMBER, 31235959);  
  2.    
  3. DayOfWeek dayOfWeek = sylvester.getDayOfWeek();  
  4. System.out.println(dayOfWeek);      // WEDNESDAY  
  5.    
  6. Month month = sylvester.getMonth();  
  7. System.out.println(month);          // DECEMBER  
  8.    
  9. long minuteOfDay = sylvester.getLong(ChronoField.MINUTE_OF_DAY);  
  10. System.out.println(minuteOfDay);    // 1439  

如果再加上的時區(qū)信息,LocalDateTime能夠被轉(zhuǎn)換成Instance實例。Instance能夠被轉(zhuǎn)換成以前的java.util.Date對象。

  1. Instant instant = sylvester  
  2.         .atZone(ZoneId.systemDefault())  
  3.         .toInstant();  
  4.    
  5. Date legacyDate = Date.from(instant);  
  6. System.out.println(legacyDate);     // Wed Dec 31 23:59:59 CET 2014 

格式化日期-時間對象就和格式化日期對象或者時間對象一樣。除了使用預(yù)定義的格式以外,我們還可以創(chuàng)建自定義的格式化對象,然后匹配我們自定義的格式。

  1. DateTimeFormatter formatter =  
  2.     DateTimeFormatter  
  3.         .ofPattern("MMM dd, yyyy - HH:mm");  
  4.    
  5. LocalDateTime parsed = LocalDateTime.parse("Nov 03, 2014 - 07:13", formatter);  
  6. String string = formatter.format(parsed);  
  7. System.out.println(string);     // Nov 03, 2014 - 07:13 

不同于java.text.NumberFormat,新的DateTimeFormatter類是不可變的,也是線程安全的。

更多的細節(jié),請看這里

Annotations

Java 8中的注解是可重復(fù)的。讓我們直接深入看看例子,弄明白它是什么意思。

首先,我們定義一個包裝注解,它包括了一個實際注解的數(shù)組

  1. @interface Hints {  
  2.     Hint[] value();  
  3. }  
  4.    
  5. @Repeatable(Hints.class)  
  6. @interface Hint {  
  7.     String value();  
只要在前面加上注解名:@Repeatable,Java 8 允許我們對同一類型使用多重注解,

變體1:使用注解容器(老方法)

  1. @Hints({@Hint("hint1"), @Hint("hint2")})  
  2. class Person {} 

變體2:使用可重復(fù)注解(新方法)

  1. @Hint("hint1")  
  2. @Hint("hint2")  
  3. class Person {} 

使用變體2,Java編譯器能夠在內(nèi)部自動對@Hint進行設(shè)置。這對于通過反射來讀取注解信息來說,是非常重要的。

  1. Hint hint = Person.class.getAnnotation(Hint.class);  
  2. System.out.println(hint);                   // null  
  3.    
  4. Hints hints1 = Person.class.getAnnotation(Hints.class);  
  5. System.out.println(hints1.value().length);  // 2  
  6.    
  7. Hint[] hints2 = Person.class.getAnnotationsByType(Hint.class);  
  8. System.out.println(hints2.length);          // 2 

盡管我們絕對不會在Person類上聲明@Hints注解,但是它的信息仍然可以通過getAnnotation(Hints.class)來讀取。并且,getAnnotationsByType方法會更方便,因為它賦予了所有@Hints注解標(biāo)注的方法直接的訪問權(quán)限。

  1. @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})  
  2. @interface MyAnnotation {} 

先到這里

我的Java 8編程指南就到此告一段落。當(dāng)然,還有很多內(nèi)容需要進一步研究和說明。這就需要靠讀者您來對JDK 8進行探究了,例如:Arrays.parallelSort, StampedLock和CompletableFuture等等 ———— 我這里只是舉幾個例子而已。

我希望這個博文能夠?qū)δ兴鶐椭?,也希望您閱讀愉快。完整的教程源代碼放在了GitHub上。您可以盡情地fork,并請通過Twitter告訴我您的反饋。

原文鏈接: winterbe 翻譯: ImportNew.com - 黃小非

譯文鏈接: http://www.importnew.com/10360.html

責(zé)任編輯:林師授 來源: ImportNew
相關(guān)推薦

2011-06-03 08:49:54

Java

2013-12-03 13:05:30

Lua腳本語言

2014-06-20 10:51:35

Linux LVM邏輯卷

2023-10-20 14:08:35

digDNS

2009-08-06 17:45:08

C# Webservi

2021-01-05 09:55:46

TmateLinux命令

2010-05-26 10:42:20

SVN1.5配置

2023-11-02 14:26:30

PyTorch機器學(xué)習(xí)

2023-11-02 14:30:25

機器學(xué)習(xí)

2009-09-02 17:38:19

C#開發(fā)GIS

2010-12-15 12:48:26

VirtualBox

2010-05-25 16:11:25

Git-SVN

2011-08-17 10:00:12

Objective-CProperty

2021-05-11 09:31:31

kustomizeoperator kubernetes

2021-05-08 09:02:48

KubeBuilderOperatork8s

2011-08-17 09:55:45

Objective-CCategory

2009-07-03 13:45:48

JSP簡明教程組件為中心

2021-03-03 12:55:30

Python列表推導(dǎo)式代碼

2010-01-26 08:25:06

F#語法F#教程

2009-08-08 16:46:39

點贊
收藏

51CTO技術(shù)棧公眾號