亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

Java 8 簡(jiǎn)明教程

testHs / 2274人閱讀

摘要:簡(jiǎn)明教程原文譯者黃小非來源簡(jiǎn)明教程并沒有沒落,人們很快就會(huì)發(fā)現(xiàn)這一點(diǎn)歡迎閱讀我編寫的介紹。編譯器會(huì)自動(dòng)地選擇合適的構(gòu)造函數(shù)來匹配函數(shù)的簽名,并選擇正確的構(gòu)造函數(shù)形式。

Java 8 簡(jiǎn)明教程

原文:Java 8 Tutorial

譯者:ImportNew.com - 黃小非

來源:Java 8簡(jiǎn)明教程

?

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

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

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

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

interface Formula {
    double calculate(int a);

    default double sqrt(int a) {
        return Math.sqrt(a);
    }
}

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

Formula formula = new Formula() {
    @Override
    public double calculate(int a) {
        return sqrt(a * 100);
    }
};

formula.calculate(100);     // 100.0
formula.sqrt(16);           // 4.0

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

Lambda表達(dá)式

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

List names = Arrays.asList("peter", "anna", "mike", "xenia");

Collections.sort(names, new Comparator() {
    @Override
    public int compare(String a, String b) {
        return b.compareTo(a);
    }
});

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

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

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

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

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

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

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

Java編譯器能夠自動(dòng)識(shí)別參數(shù)的類型,所以你就可以省略掉類型不寫。讓我們?cè)偕钊氲匮芯恳幌耹ambda表達(dá)式的威力吧。

函數(shù)式接口

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

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

舉例:

@FunctionalInterface
interface Converter {
    T convert(F from);
}

Converter converter = (from) -> Integer.valueOf(from);
Integer converted = converter.convert("123");
System.out.println(converted);    // 123

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

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

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

Converter converter = Integer::valueOf;
Integer converted = converter.convert("123");
System.out.println(converted);   // 123

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

class Something {
    String startsWith(String s) {
        return String.valueOf(s.charAt(0));
    }
}

Something something = new Something();
Converter converter = something::startsWith;
String converted = converter.convert("Java");
System.out.println(converted);    // "J"

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

class Person {
    String firstName;
    String lastName;

    Person() {}

    Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

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

interface PersonFactory

{ P create(String firstName, String lastName); }

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

PersonFactory personFactory = Person::new;
Person person = personFactory.create("Peter", "Parker");

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

Lambda的范圍

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

訪問局部變量

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

final int num = 1;
Converter stringConverter =
        (from) -> String.valueOf(from + num);

stringConverter.convert(2);     // 3

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

int num = 1;
Converter stringConverter =
        (from) -> String.valueOf(from + num);

stringConverter.convert(2);     // 3

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

int num = 1;
Converter stringConverter =
        (from) -> String.valueOf(from + num);
num = 3;

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

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

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

class Lambda4 {
    static int outerStaticNum;
    int outerNum;

    void testScopes() {
        Converter stringConverter1 = (from) -> {
            outerNum = 23;
            return String.valueOf(from);
        };

        Converter stringConverter2 = (from) -> {
            outerStaticNum = 72;
            return String.valueOf(from);
        };
    }
}
訪問默認(rèn)接口方法

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

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

Formula formula = (a) -> sqrt( a * 100);
內(nèi)置函數(shù)式接口

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

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

Predicates

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

Predicate predicate = (s) -> s.length() > 0;

predicate.test("foo");              // true
predicate.negate().test("foo");     // false

Predicate nonNull = Objects::nonNull;
Predicate isNull = Objects::isNull;

Predicate isEmpty = String::isEmpty;
Predicate isNotEmpty = isEmpty.negate();
Functions

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

Function toInteger = Integer::valueOf;
Function backToString = toInteger.andThen(String::valueOf);

backToString.apply("123");     // "123"
Suppliers

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

Supplier personSupplier = Person::new;
personSupplier.get();   // new Person
Consumers

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

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

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

Comparator comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);

Person p1 = new Person("John", "Doe");
Person p2 = new Person("Alice", "Wonderland");

comparator.compare(p1, p2);             // > 0
comparator.reversed().compare(p1, p2);  // < 0
Optionals

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

Optional是一個(gè)簡(jiǎn)單的值容器,這個(gè)值可以是null,也可以是non-null。考慮到一個(gè)方法可能會(huì)返回一個(gè)non-null的值,也可能返回一個(gè)空值。為了不直接返回null,我們?cè)贘ava 8中就返回一個(gè)Optional.

Optional optional = Optional.of("bam");

optional.isPresent();           // true
optional.get();                 // "bam"
optional.orElse("fallback");    // "bam"

optional.ifPresent((s) -> System.out.println(s.charAt(0)));     // "b"
Streams

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

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

List stringCollection = new ArrayList<>();
stringCollection.add("ffffd2");
stringCollection.add("aaa2");
stringCollection.add("bbb1");
stringCollection.add("aaa1");
stringCollection.add("bbb3");
stringCollection.add("ccc");
stringCollection.add("bbb2");
stringCollection.add("ffffd1");

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

Filter

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

stringCollection
    .stream()
    .filter((s) -> s.startsWith("a"))
    .forEach(System.out::println);

// "aaa2", "aaa1"
Sorted

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

stringCollection
    .stream()
    .sorted()
    .filter((s) -> s.startsWith("a"))
    .forEach(System.out::println);

// "aaa1", "aaa2"

一定要記住,sorted只是創(chuàng)建一個(gè)流對(duì)象排序的視圖,而不會(huì)改變?cè)瓉砑现性氐捻樞?。原來string集合中的元素順序是沒有改變的。

System.out.println(stringCollection);
// ffffd2, aaa2, bbb1, aaa1, bbb3, ccc, bbb2, ffffd1
Map

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

stringCollection
    .stream()
    .map(String::toUpperCase)
    .sorted((a, b) -> b.compareTo(a))
    .forEach(System.out::println);

// "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"
Match

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

boolean anyStartsWithA =
    stringCollection
        .stream()
        .anyMatch((s) -> s.startsWith("a"));

System.out.println(anyStartsWithA);      // true

boolean allStartsWithA =
    stringCollection
        .stream()
        .allMatch((s) -> s.startsWith("a"));

System.out.println(allStartsWithA);      // false

boolean noneStartsWithZ =
    stringCollection
        .stream()
        .noneMatch((s) -> s.startsWith("z"));

System.out.println(noneStartsWithZ);      // true
Count

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

long startsWithB =
    stringCollection
        .stream()
        .filter((s) -> s.startsWith("b"))
        .count();

System.out.println(startsWithB);    // 3
Reduce

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

Optional reduced =
    stringCollection
        .stream()
        .sorted()
        .reduce((s1, s2) -> s1 + "#" + s2);

reduced.ifPresent(System.out::println);
// "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ffffd1#ffffd2"
Parallel Streams

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

下面的例子就演示了如何使用并行流進(jìn)行操作來提高運(yùn)行效率,代碼非常簡(jiǎn)單。

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

int max = 1000000;
List values = new ArrayList<>(max);
for (int i = 0; i < max; i++) {
    UUID uuid = UUID.randomUUID();
    values.add(uuid.toString());
}

現(xiàn)在,我們測(cè)量一下對(duì)這個(gè)集合進(jìn)行排序所使用的時(shí)間。

順序排序
long t0 = System.nanoTime();

long count = values.stream().sorted().count();
System.out.println(count);

long t1 = System.nanoTime();

long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
System.out.println(String.format("sequential sort took: %d ms", millis));

// sequential sort took: 899 ms
并行排序
long t0 = System.nanoTime();

long count = values.parallelStream().sorted().count();
System.out.println(count);

long t1 = System.nanoTime();

long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
System.out.println(String.format("parallel sort took: %d ms", millis));

// parallel sort took: 472 ms

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

Map

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

Map map = new HashMap<>();

for (int i = 0; i < 10; i++) {
    map.putIfAbsent(i, "val" + i);
}

map.forEach((id, val) -> System.out.println(val));

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

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

map.computeIfPresent(3, (num, val) -> val + num);
map.get(3);             // val33

map.computeIfPresent(9, (num, val) -> null);
map.containsKey(9);     // false

map.computeIfAbsent(23, num -> "val" + num);
map.containsKey(23);    // true

map.computeIfAbsent(3, num -> "bam");
map.get(3);             // val33

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

map.remove(3, "val3");
map.get(3);             // val33

map.remove(3, "val33");
map.get(3);             // null

另一個(gè)有用的方法:

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

將map中的實(shí)例合并也是非常容易的:

map.merge(9, "val9", (value, newValue) -> value.concat(newValue));
map.get(9);             // val9

map.merge(9, "concat", (value, newValue) -> value.concat(newValue));
map.get(9);             // val9concat

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

時(shí)間日期API

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

Clock

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

Clock clock = Clock.systemDefaultZone();
long millis = clock.millis();

Instant instant = clock.instant();
Date legacyDate = Date.from(instant);   // legacy java.util.Date
Timezones

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

System.out.println(ZoneId.getAvailableZoneIds());
// prints all available timezone ids

ZoneId zone1 = ZoneId.of("Europe/Berlin");
ZoneId zone2 = ZoneId.of("Brazil/East");
System.out.println(zone1.getRules());
System.out.println(zone2.getRules());

// ZoneRules[currentStandardOffset=+01:00]
// ZoneRules[currentStandardOffset=-03:00]
LocalTime

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

LocalTime now1 = LocalTime.now(zone1);
LocalTime now2 = LocalTime.now(zone2);

System.out.println(now1.isBefore(now2));  // false

long hoursBetween = ChronoUnit.HOURS.between(now1, now2);
long minutesBetween = ChronoUnit.MINUTES.between(now1, now2);

System.out.println(hoursBetween);       // -3
System.out.println(minutesBetween);     // -239

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

LocalTime late = LocalTime.of(23, 59, 59);
System.out.println(late);       // 23:59:59

DateTimeFormatter germanFormatter =
    DateTimeFormatter
        .ofLocalizedTime(FormatStyle.SHORT)
        .withLocale(Locale.GERMAN);

LocalTime leetTime = LocalTime.parse("13:37", germanFormatter);
System.out.println(leetTime);   // 13:37
LocalDate

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

LocalDate today = LocalDate.now();
LocalDate tomorrow = today.plus(1, ChronoUnit.DAYS);
LocalDate yesterday = tomorrow.minusDays(2);

LocalDate independenceDay = LocalDate.of(2014, Month.JULY, 4);
DayOfWeek dayOfWeek = independenceDay.getDayOfWeek();
System.out.println(dayOfWeek);    // FRIDAYParsing a LocalDate from a string is just as simple as parsing a LocalTime:

解析字符串并形成LocalDate對(duì)象,這個(gè)操作和解析LocalTime一樣簡(jiǎn)單。

DateTimeFormatter germanFormatter =
    DateTimeFormatter
        .ofLocalizedDate(FormatStyle.MEDIUM)
        .withLocale(Locale.GERMAN);

LocalDate xmas = LocalDate.parse("24.12.2014", germanFormatter);
System.out.println(xmas);   // 2014-12-24
LocalDateTime

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

LocalDateTime sylvester = LocalDateTime.of(2014, Month.DECEMBER, 31, 23, 59, 59);

DayOfWeek dayOfWeek = sylvester.getDayOfWeek();
System.out.println(dayOfWeek);      // WEDNESDAY

Month month = sylvester.getMonth();
System.out.println(month);          // DECEMBER

long minuteOfDay = sylvester.getLong(ChronoField.MINUTE_OF_DAY);
System.out.println(minuteOfDay);    // 1439

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

Instant instant = sylvester
        .atZone(ZoneId.systemDefault())
        .toInstant();

Date legacyDate = Date.from(instant);
System.out.println(legacyDate);     // Wed Dec 31 23:59:59 CET 2014

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

DateTimeFormatter formatter =
    DateTimeFormatter
        .ofPattern("MMM dd, yyyy - HH:mm");

LocalDateTime parsed = LocalDateTime.parse("Nov 03, 2014 - 07:13", formatter);
String string = formatter.format(parsed);
System.out.println(string);     // Nov 03, 2014 - 07:13

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

更多的細(xì)節(jié),請(qǐng)看這里

Annotations

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

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

@interface Hints {
    Hint[] value();
}

@Repeatable(Hints.class)
@interface Hint {
    String value();
}

只要在前面加上注解名:@Repeatable,Java 8 允許我們對(duì)同一類型使用多重注解,

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

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

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

@Hint("hint1")
@Hint("hint2")
class Person {}

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

Hint hint = Person.class.getAnnotation(Hint.class);
System.out.println(hint);                   // null

Hints hints1 = Person.class.getAnnotation(Hints.class);
System.out.println(hints1.value().length);  // 2

Hint[] hints2 = Person.class.getAnnotationsByType(Hint.class);
System.out.println(hints2.length);          // 2

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

@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
@interface MyAnnotation {}
先到這里

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

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

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/64964.html

相關(guān)文章

  • Java核心技術(shù)教程整理,長期更新

    以下是Java技術(shù)棧微信公眾號(hào)發(fā)布的關(guān)于 Java 的技術(shù)干貨,從以下幾個(gè)方面匯總。 Java 基礎(chǔ)篇 Java 集合篇 Java 多線程篇 Java JVM篇 Java 進(jìn)階篇 Java 新特性篇 Java 工具篇 Java 書籍篇 Java基礎(chǔ)篇 8張圖帶你輕松溫習(xí) Java 知識(shí) Java父類強(qiáng)制轉(zhuǎn)換子類原則 一張圖搞清楚 Java 異常機(jī)制 通用唯一標(biāo)識(shí)碼UUID的介紹及使用 字符串...

    Anchorer 評(píng)論0 收藏0
  • Java 8 并發(fā)教程:線程和執(zhí)行器

    摘要:在這個(gè)示例中我們使用了一個(gè)單線程線程池的。在延遲消逝后,任務(wù)將會(huì)并發(fā)執(zhí)行。這是并發(fā)系列教程的第一部分。第一部分線程和執(zhí)行器第二部分同步和鎖第三部分原子操作和 Java 8 并發(fā)教程:線程和執(zhí)行器 原文:Java 8 Concurrency Tutorial: Threads and Executors 譯者:BlankKelly 來源:Java8并發(fā)教程:Threads和Execut...

    jsdt 評(píng)論0 收藏0
  • [譯] Java 8 Nashorn 教程

    摘要:未來的主要發(fā)布基于。在中調(diào)用函數(shù)支持從代碼中直接調(diào)用定義在腳本文件中的函數(shù)。下面的函數(shù)稍后會(huì)在端調(diào)用為了調(diào)用函數(shù),你首先需要將腳本引擎轉(zhuǎn)換為。調(diào)用函數(shù)將結(jié)果輸出到,所以我們會(huì)首先看到輸出。幸運(yùn)的是,有一套補(bǔ)救措施。 原文:Java 8 Nashorn Tutorial 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 這個(gè)教程中,你會(huì)通過簡(jiǎn)單易懂的代碼示例,來了解Nashorn Ja...

    _ivan 評(píng)論0 收藏0
  • Java 8 并發(fā)教程:原子變量和 ConcurrentMa

    摘要:并發(fā)教程原子變量和原文譯者飛龍協(xié)議歡迎閱讀我的多線程編程系列教程的第三部分。如果你能夠在多線程中同時(shí)且安全地執(zhí)行某個(gè)操作,而不需要關(guān)鍵字或上一章中的鎖,那么這個(gè)操作就是原子的。當(dāng)多線程的更新比讀取更頻繁時(shí),這個(gè)類通常比原子數(shù)值類性能更好。 Java 8 并發(fā)教程:原子變量和 ConcurrentMap 原文:Java 8 Concurrency Tutorial: Synchroni...

    bitkylin 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<