<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                # Java 8 Tutorial 歡迎閱讀我對Java 8的介紹。本教程將逐步指導您完成所有新語言功能。 在簡短的代碼示例的基礎上,您將學習如何使用默認接口方法,lambda表達式,方法引用和可重復注釋。 在本文的最后,您將熟悉最新的 API 更改,如流,函數式接口(Functional Interfaces),Map 類的擴展和新的 Date API。 沒有大段枯燥的文字,只有一堆注釋的代碼片段。 ### 接口的默認方法(Default Methods for Interfaces) Java 8使我們能夠通過使用`default`關鍵字向接口添加非抽象方法實現。 此功能也稱為[虛擬擴展方法](http://stackoverflow.com/a/24102730)。 第一個例子: ~~~java interface Formula{ double calculate(int a); default double sqrt(int a) { return Math.sqrt(a); } } ~~~ Formula 接口中除了抽象方法計算接口公式還定義了默認方法`sqrt`。 實現該接口的類只需要實現抽象方法`calculate`。 默認方法`sqrt`可以直接使用。當然你也可以直接通過接口創建對象,然后實現接口中的默認方法就可以了,我們通過代碼演示一下這種方式。 ~~~java public class Main { public static void main(String[] args) { // TODO 通過匿名內部類方式訪問接口 Formula formula = new Formula() { @Override public double calculate(int a) { return sqrt(a * 100); } }; System.out.println(formula.calculate(100)); // 100.0 System.out.println(formula.sqrt(16)); // 4.0 } } ~~~ formula 是作為匿名對象實現的。該代碼非常容易理解,6行代碼實現了計算`sqrt(a * 100)`。在下一節中,我們將會看到在 Java 8 中實現單個方法對象有一種更好更方便的方法。 **譯者注:**不管是抽象類還是接口,都可以通過匿名內部類的方式訪問。不能通過抽象類或者接口直接創建對象。對于上面通過匿名內部類方式訪問接口,我們可以這樣理解:一個內部類實現了接口里的抽象方法并且返回一個內部類對象,之后我們讓接口的引用來指向這個對象。 ### Lambda表達式(Lambda expressions) 首先看看在老版本的Java中是如何排列字符串的: ~~~java List<String> names = Arrays.asList("peter", "anna", "mike", "xenia"); Collections.sort(names, new Comparator<String>() { @Override public int compare(String a, String b) { return b.compareTo(a); } }); ~~~ 只需要給靜態方法`Collections.sort`傳入一個 List 對象以及一個比較器來按指定順序排列。通常做法都是創建一個匿名的比較器對象然后將其傳遞給`sort`方法。 在Java 8 中你就沒必要使用這種傳統的匿名對象的方式了,Java 8提供了更簡潔的語法,lambda表達式: ~~~java Collections.sort(names, (String a, String b) -> { return b.compareTo(a); }); ~~~ 可以看出,代碼變得更段且更具有可讀性,但是實際上還可以寫得更短: ~~~java Collections.sort(names, (String a, String b) -> b.compareTo(a)); ~~~ 對于函數體只有一行代碼的,你可以去掉大括號{}以及return關鍵字,但是你還可以寫得更短點: ~~~java names.sort((a, b) -> b.compareTo(a)); ~~~ List 類本身就有一個`sort`方法。并且Java編譯器可以自動推導出參數類型,所以你可以不用再寫一次類型。接下來我們看看lambda表達式還有什么其他用法。 ### 函數式接口(Functional Interfaces) **譯者注:**原文對這部分解釋不太清楚,故做了修改! Java 語言設計者們投入了大量精力來思考如何使現有的函數友好地支持Lambda。最終采取的方法是:增加函數式接口的概念。**“函數式接口”是指僅僅只包含一個抽象方法,但是可以有多個非抽象方法(也就是上面提到的默認方法)的接口。**像這樣的接口,可以被隱式轉換為lambda表達式。`java.lang.Runnable`與`java.util.concurrent.Callable`是函數式接口最典型的兩個例子。Java 8增加了一種特殊的注解`@FunctionalInterface`,但是這個注解通常不是必須的(某些情況建議使用),只要接口只包含一個抽象方法,虛擬機會自動判斷該接口為函數式接口。一般建議在接口上使用`@FunctionalInterface`注解進行聲明,這樣的話,編譯器如果發現你標注了這個注解的接口有多于一個抽象方法的時候會報錯的,如下圖所示 [![@FunctionalInterface 注解](https://camo.githubusercontent.com/1fa0456a959db0100192af7a9699b32329a6b33d/68747470733a2f2f6d792d626c6f672d746f2d7573652e6f73732d636e2d6265696a696e672e616c6979756e63732e636f6d2f323031392d322f4046756e6374696f6e616c496e746572666163652e706e67)](https://camo.githubusercontent.com/1fa0456a959db0100192af7a9699b32329a6b33d/68747470733a2f2f6d792d626c6f672d746f2d7573652e6f73732d636e2d6265696a696e672e616c6979756e63732e636f6d2f323031392d322f4046756e6374696f6e616c496e746572666163652e706e67) 示例: ~~~java @FunctionalInterface public interface Converter<F, T> { T convert(F from); } ~~~ ~~~java // TODO 將數字字符串轉換為整數類型 Converter<String, Integer> converter = (from) -> Integer.valueOf(from); Integer converted = converter.convert("123"); System.out.println(converted.getClass()); //class java.lang.Integer ~~~ **譯者注:**大部分函數式接口都不用我們自己寫,Java8都給我們實現好了,這些接口都在java.util.function包里。 ### 方法和構造函數引用(Method and Constructor References) 前一節中的代碼還可以通過靜態方法引用來表示: ~~~java Converter<String, Integer> converter = Integer::valueOf; Integer converted = converter.convert("123"); System.out.println(converted.getClass()); //class java.lang.Integer ~~~ Java 8允許您通過`::`關鍵字傳遞方法或構造函數的引用。 上面的示例顯示了如何引用靜態方法。 但我們也可以引用對象方法: ~~~java class Something { String startsWith(String s) { return String.valueOf(s.charAt(0)); } } ~~~ ~~~java Something something = new Something(); Converter<String, String> converter = something::startsWith; String converted = converter.convert("Java"); System.out.println(converted); // "J" ~~~ 接下來看看構造函數是如何使用`::`關鍵字來引用的,首先我們定義一個包含多個構造函數的簡單類: ~~~java class Person { String firstName; String lastName; Person() {} Person(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } } ~~~ 接下來我們指定一個用來創建Person對象的對象工廠接口: ~~~java interface PersonFactory<P extends Person> { P create(String firstName, String lastName); } ~~~ 這里我們使用構造函數引用來將他們關聯起來,而不是手動實現一個完整的工廠: ~~~java PersonFactory<Person> personFactory = Person::new; Person person = personFactory.create("Peter", "Parker"); ~~~ 我們只需要使用`Person::new`來獲取Person類構造函數的引用,Java編譯器會自動根據`PersonFactory.create`方法的參數類型來選擇合適的構造函數。 ### Lamda 表達式作用域(Lambda Scopes) #### 訪問局部變量 我們可以直接在 lambda 表達式中訪問外部的局部變量: ~~~java final int num = 1; Converter<Integer, String> stringConverter = (from) -> String.valueOf(from + num); stringConverter.convert(2); // 3 ~~~ 但是和匿名對象不同的是,這里的變量num可以不用聲明為final,該代碼同樣正確: ~~~java int num = 1; Converter<Integer, String> stringConverter = (from) -> String.valueOf(from + num); stringConverter.convert(2); // 3 ~~~ 不過這里的 num 必須不可被后面的代碼修改(即隱性的具有final的語義),例如下面的就無法編譯: ~~~java int num = 1; Converter<Integer, String> stringConverter = (from) -> String.valueOf(from + num); num = 3;//在lambda表達式中試圖修改num同樣是不允許的。 ~~~ #### 訪問字段和靜態變量 與局部變量相比,我們對lambda表達式中的實例字段和靜態變量都有讀寫訪問權限。 該行為和匿名對象是一致的。 ~~~java class Lambda4 { static int outerStaticNum; int outerNum; void testScopes() { Converter<Integer, String> stringConverter1 = (from) -> { outerNum = 23; return String.valueOf(from); }; Converter<Integer, String> stringConverter2 = (from) -> { outerStaticNum = 72; return String.valueOf(from); }; } } ~~~ #### 訪問默認接口方法 還記得第一節中的 formula 示例嗎?`Formula`接口定義了一個默認方法`sqrt`,可以從包含匿名對象的每個 formula 實例訪問該方法。 這不適用于lambda表達式。 無法從 lambda 表達式中訪問默認方法,故以下代碼無法編譯: ~~~java Formula formula = (a) -> sqrt(a * 100); ~~~ ### 內置函數式接口(Built-in Functional Interfaces) JDK 1.8 API包含許多內置函數式接口。 其中一些借口在老版本的 Java 中是比較常見的比如:`Comparator`或`Runnable`,這些接口都增加了`@FunctionalInterface`注解以便能用在 lambda 表達式上。 但是 Java 8 API 同樣還提供了很多全新的函數式接口來讓你的編程工作更加方便,有一些接口是來自[Google Guava](https://code.google.com/p/guava-libraries/)庫里的,即便你對這些很熟悉了,還是有必要看看這些是如何擴展到lambda上使用的。 #### Predicates Predicate 接口是只有一個參數的返回布爾類型值的**斷言型**接口。該接口包含多種默認方法來將 Predicate 組合成其他復雜的邏輯(比如:與,或,非): **譯者注:**Predicate 接口源碼如下 ~~~java package java.util.function; import java.util.Objects; @FunctionalInterface public interface Predicate<T> { // 該方法是接受一個傳入類型,返回一個布爾值.此方法應用于判斷. boolean test(T t); //and方法與關系型運算符"&&"相似,兩邊都成立才返回true default Predicate<T> and(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) && other.test(t); } // 與關系運算符"!"相似,對判斷進行取反 default Predicate<T> negate() { return (t) -> !test(t); } //or方法與關系型運算符"||"相似,兩邊只要有一個成立就返回true default Predicate<T> or(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) || other.test(t); } // 該方法接收一個Object對象,返回一個Predicate類型.此方法用于判斷第一個test的方法與第二個test方法相同(equal). static <T> Predicate<T> isEqual(Object targetRef) { return (null == targetRef) ? Objects::isNull : object -> targetRef.equals(object); } ~~~ 示例: ~~~java Predicate<String> predicate = (s) -> s.length() > 0; predicate.test("foo"); // true predicate.negate().test("foo"); // false Predicate<Boolean> nonNull = Objects::nonNull; Predicate<Boolean> isNull = Objects::isNull; Predicate<String> isEmpty = String::isEmpty; Predicate<String> isNotEmpty = isEmpty.negate(); ~~~ #### Functions Function 接口接受一個參數并生成結果。默認方法可用于將多個函數鏈接在一起(compose, andThen): **譯者注:**Function 接口源碼如下 ~~~java package java.util.function; import java.util.Objects; @FunctionalInterface public interface Function<T, R> { //將Function對象應用到輸入的參數上,然后返回計算結果。 R apply(T t); //將兩個Function整合,并返回一個能夠執行兩個Function對象功能的Function對象。 default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } // default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } static <T> Function<T, T> identity() { return t -> t; } } ~~~ ~~~java Function<String, Integer> toInteger = Integer::valueOf; Function<String, String> backToString = toInteger.andThen(String::valueOf); backToString.apply("123"); // "123" ~~~ #### Suppliers Supplier 接口產生給定泛型類型的結果。 與 Function 接口不同,Supplier 接口不接受參數。 ~~~java Supplier<Person> personSupplier = Person::new; personSupplier.get(); // new Person ~~~ #### Consumers Consumer 接口表示要對單個輸入參數執行的操作。 ~~~java Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName); greeter.accept(new Person("Luke", "Skywalker")); ~~~ #### Comparators Comparator 是老Java中的經典接口, Java 8在此之上添加了多種默認方法: ~~~java Comparator<Person> 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 Optionals不是函數式接口,而是用于防止 NullPointerException 的漂亮工具。這是下一節的一個重要概念,讓我們快速了解一下Optionals的工作原理。 Optional 是一個簡單的容器,其值可能是null或者不是null。在Java 8之前一般某個函數應該返回非空對象但是有時卻什么也沒有返回,而在Java 8中,你應該返回 Optional 而不是 null。 譯者注:示例中每個方法的作用已經添加。 ~~~java //of():為非null的值創建一個Optional Optional<String> optional = Optional.of("bam"); // isPresent(): 如果值存在返回true,否則返回false optional.isPresent(); // true //get():如果Optional有值則將其返回,否則拋出NoSuchElementException optional.get(); // "bam" //orElse():如果有值則將其返回,否則返回指定的其它值 optional.orElse("fallback"); // "bam" //ifPresent():如果Optional實例有值則為其調用consumer,否則不做處理 optional.ifPresent((s) -> System.out.println(s.charAt(0))); // "b" ~~~ 推薦閱讀:[\[Java8\]如何正確使用Optional](https://blog.kaaass.net/archives/764) ## Streams(流) `java.util.Stream`表示能應用在一組元素上一次執行的操作序列。Stream 操作分為中間操作或者最終操作兩種,最終操作返回一特定類型的計算結果,而中間操作返回Stream本身,這樣你就可以將多個操作依次串起來。Stream 的創建需要指定一個數據源,比如`java.util.Collection`的子類,List 或者 Set, Map 不支持。Stream 的操作可以串行執行或者并行執行。 首先看看Stream是怎么用,首先創建實例代碼的用到的數據List: ~~~java List<String> stringCollection = new ArrayList<>(); stringCollection.add("ddd2"); stringCollection.add("aaa2"); stringCollection.add("bbb1"); stringCollection.add("aaa1"); stringCollection.add("bbb3"); stringCollection.add("ccc"); stringCollection.add("bbb2"); stringCollection.add("ddd1"); ~~~ Java 8擴展了集合類,可以通過 Collection.stream() 或者 Collection.parallelStream() 來創建一個Stream。下面幾節將詳細解釋常用的Stream操作: ### Filter(過濾) 過濾通過一個predicate接口來過濾并只保留符合條件的元素,該操作屬于**中間操作**,所以我們可以在過濾后的結果來應用其他Stream操作(比如forEach)。forEach需要一個函數來對過濾后的元素依次執行。forEach是一個最終操作,所以我們不能在forEach之后來執行其他Stream操作。 ~~~java // 測試 Filter(過濾) stringList .stream() .filter((s) -> s.startsWith("a")) .forEach(System.out::println);//aaa2 aaa1 ~~~ forEach 是為 Lambda 而設計的,保持了最緊湊的風格。而且 Lambda 表達式本身是可以重用的,非常方便。 ### Sorted(排序) 排序是一個**中間操作**,返回的是排序好后的 Stream。**如果你不指定一個自定義的 Comparator 則會使用默認排序。** ~~~java // 測試 Sort (排序) stringList .stream() .sorted() .filter((s) -> s.startsWith("a")) .forEach(System.out::println);// aaa1 aaa2 ~~~ 需要注意的是,排序只創建了一個排列好后的Stream,而不會影響原有的數據源,排序之后原數據stringCollection是不會被修改的: ~~~java System.out.println(stringList);// ddd2, aaa2, bbb1, aaa1, bbb3, ccc, bbb2, ddd1 ~~~ ### Map(映射) 中間操作 map 會將元素根據指定的 Function 接口來依次將元素轉成另外的對象。 下面的示例展示了將字符串轉換為大寫字符串。你也可以通過map來講對象轉換成其他類型,map返回的Stream類型是根據你map傳遞進去的函數的返回值決定的。 ~~~java // 測試 Map 操作 stringList .stream() .map(String::toUpperCase) .sorted((a, b) -> b.compareTo(a)) .forEach(System.out::println);// "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1" ~~~ ### Match(匹配) Stream提供了多種匹配操作,允許檢測指定的Predicate是否匹配整個Stream。所有的匹配操作都是**最終操作**,并返回一個 boolean 類型的值。 ~~~java // 測試 Match (匹配)操作 boolean anyStartsWithA = stringList .stream() .anyMatch((s) -> s.startsWith("a")); System.out.println(anyStartsWithA); // true boolean allStartsWithA = stringList .stream() .allMatch((s) -> s.startsWith("a")); System.out.println(allStartsWithA); // false boolean noneStartsWithZ = stringList .stream() .noneMatch((s) -> s.startsWith("z")); System.out.println(noneStartsWithZ); // true ~~~ ### Count(計數) 計數是一個**最終操作**,返回Stream中元素的個數,**返回值類型是 long**。 ~~~java //測試 Count (計數)操作 long startsWithB = stringList .stream() .filter((s) -> s.startsWith("b")) .count(); System.out.println(startsWithB); // 3 ~~~ ### Reduce(規約) 這是一個**最終操作**,允許通過指定的函數來講stream中的多個元素規約為一個元素,規約后的結果是通過Optional 接口表示的: ~~~java //測試 Reduce (規約)操作 Optional<String> reduced = stringList .stream() .sorted() .reduce((s1, s2) -> s1 + "#" + s2); reduced.ifPresent(System.out::println);//aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2 ~~~ **譯者注:**這個方法的主要作用是把 Stream 元素組合起來。它提供一個起始值(種子),然后依照運算規則(BinaryOperator),和前面 Stream 的第一個、第二個、第 n 個元素組合。從這個意義上說,字符串拼接、數值的 sum、min、max、average 都是特殊的 reduce。例如 Stream 的 sum 就相當于`Integer sum = integers.reduce(0, (a, b) -> a+b);`也有沒有起始值的情況,這時會把 Stream 的前面兩個元素組合起來,返回的是 Optional。 ~~~java // 字符串連接,concat = "ABCD" String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat); // 求最小值,minValue = -3.0 double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min); // 求和,sumValue = 10, 有起始值 int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum); // 求和,sumValue = 10, 無起始值 sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get(); // 過濾,字符串連接,concat = "ace" concat = Stream.of("a", "B", "c", "D", "e", "F"). filter(x -> x.compareTo("Z") > 0). reduce("", String::concat); ~~~ 上面代碼例如第一個示例的 reduce(),第一個參數(空白字符)即為起始值,第二個參數(String::concat)為 BinaryOperator。這類有起始值的 reduce() 都返回具體的對象。而對于第四個示例沒有起始值的 reduce(),由于可能沒有足夠的元素,返回的是 Optional,請留意這個區別。更多內容查看:[IBM:Java 8 中的 Streams API 詳解](https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/index.html) ## Parallel Streams(并行流) 前面提到過Stream有串行和并行兩種,串行Stream上的操作是在一個線程中依次完成,而并行Stream則是在多個線程上同時執行。 下面的例子展示了是如何通過并行Stream來提升性能: 首先我們創建一個沒有重復元素的大表: ~~~java int max = 1000000; List<String> values = new ArrayList<>(max); for (int i = 0; i < max; i++) { UUID uuid = UUID.randomUUID(); values.add(uuid.toString()); } ~~~ 我們分別用串行和并行兩種方式對其進行排序,最后看看所用時間的對比。 ### Sequential Sort(串行排序) ~~~java //串行排序 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)); ~~~ ~~~ 1000000 sequential sort took: 709 ms//串行排序所用的時間 ~~~ ### Parallel Sort(并行排序) ~~~java //并行排序 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)); ~~~ ~~~java 1000000 parallel sort took: 475 ms//串行排序所用的時間 ~~~ 上面兩個代碼幾乎是一樣的,但是并行版的快了 50% 左右,唯一需要做的改動就是將`stream()`改為`parallelStream()`。 ## Maps 前面提到過,Map 類型不支持 streams,不過Map提供了一些新的有用的方法來處理一些日常任務。Map接口本身沒有可用的`stream()`方法,但是你可以在鍵,值上創建專門的流或者通過`map.keySet().stream()`,`map.values().stream()`和`map.entrySet().stream()`。 此外,Maps 支持各種新的和有用的方法來執行常見任務。 ~~~java Map<Integer, String> map = new HashMap<>(); for (int i = 0; i < 10; i++) { map.putIfAbsent(i, "val" + i); } map.forEach((id, val) -> System.out.println(val));//val0 val1 val2 val3 val4 val5 val6 val7 val8 val9 ~~~ `putIfAbsent`阻止我們在null檢查時寫入額外的代碼;`forEach`接受一個 consumer 來對 map 中的每個元素操作。 此示例顯示如何使用函數在 map 上計算代碼: ~~~java 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 ~~~ 接下來展示如何在Map里刪除一個鍵值全都匹配的項: ~~~java map.remove(3, "val3"); map.get(3); // val33 map.remove(3, "val33"); map.get(3); // null ~~~ 另外一個有用的方法: ~~~java map.getOrDefault(42, "not found"); // not found ~~~ 對Map的元素做合并也變得很容易了: ~~~java 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 ~~~ Merge 做的事情是如果鍵名不存在則插入,否則則對原鍵對應的值做合并操作并重新插入到map中。 ## Date API(日期相關API) Java 8在`java.time`包下包含一個全新的日期和時間API。新的Date API與Joda-Time庫相似,但它們不一樣。以下示例涵蓋了此新 API 的最重要部分。譯者對這部分內容參考相關書籍做了大部分修改。 **譯者注(總結):** * Clock 類提供了訪問當前日期和時間的方法,Clock 是時區敏感的,可以用來取代`System.currentTimeMillis()`來獲取當前的微秒數。某一個特定的時間點也可以使用`Instant`類來表示,`Instant`類也可以用來創建舊版本的`java.util.Date`對象。 * 在新API中時區使用 ZoneId 來表示。時區可以很方便的使用靜態方法of來獲取到。 抽象類`ZoneId`(在`java.time`包中)表示一個區域標識符。 它有一個名為`getAvailableZoneIds`的靜態方法,它返回所有區域標識符。 * jdk1.8中新增了 LocalDate 與 LocalDateTime等類來解決日期處理方法,同時引入了一個新的類DateTimeFormatter 來解決日期格式化問題。可以使用Instant代替 Date,LocalDateTime代替 Calendar,DateTimeFormatter 代替 SimpleDateFormat。 ### Clock Clock 類提供了訪問當前日期和時間的方法,Clock 是時區敏感的,可以用來取代`System.currentTimeMillis()`來獲取當前的微秒數。某一個特定的時間點也可以使用`Instant`類來表示,`Instant`類也可以用來創建舊版本的`java.util.Date`對象。 ~~~java Clock clock = Clock.systemDefaultZone(); long millis = clock.millis(); System.out.println(millis);//1552379579043 Instant instant = clock.instant(); System.out.println(instant); Date legacyDate = Date.from(instant); //2019-03-12T08:46:42.588Z System.out.println(legacyDate);//Tue Mar 12 16:32:59 CST 2019 ~~~ ### Timezones(時區) 在新API中時區使用 ZoneId 來表示。時區可以很方便的使用靜態方法of來獲取到。 抽象類`ZoneId`(在`java.time`包中)表示一個區域標識符。 它有一個名為`getAvailableZoneIds`的靜態方法,它返回所有區域標識符。 ~~~java //輸出所有區域標識符 System.out.println(ZoneId.getAvailableZoneIds()); ZoneId zone1 = ZoneId.of("Europe/Berlin"); ZoneId zone2 = ZoneId.of("Brazil/East"); System.out.println(zone1.getRules());// ZoneRules[currentStandardOffset=+01:00] System.out.println(zone2.getRules());// ZoneRules[currentStandardOffset=-03:00] ~~~ ### LocalTime(本地時間) LocalTime 定義了一個沒有時區信息的時間,例如 晚上10點或者 17:30:15。下面的例子使用前面代碼創建的時區創建了兩個本地時間。之后比較時間并以小時和分鐘為單位計算兩個時間的時間差: ~~~java 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 提供了多種工廠方法來簡化對象的創建,包括解析時間字符串. ~~~java 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(本地日期) LocalDate 表示了一個確切的日期,比如 2014-03-11。該對象值是不可變的,用起來和LocalTime基本一致。下面的例子展示了如何給Date對象加減天/月/年。另外要注意的是這些對象是不可變的,操作返回的總是一個新實例。 ~~~java LocalDate today = LocalDate.now();//獲取現在的日期 System.out.println("今天的日期: "+today);//2019-03-12 LocalDate tomorrow = today.plus(1, ChronoUnit.DAYS); System.out.println("明天的日期: "+tomorrow);//2019-03-13 LocalDate yesterday = tomorrow.minusDays(2); System.out.println("昨天的日期: "+yesterday);//2019-03-11 LocalDate independenceDay = LocalDate.of(2019, Month.MARCH, 12); DayOfWeek dayOfWeek = independenceDay.getDayOfWeek(); System.out.println("今天是周幾:"+dayOfWeek);//TUESDAY ~~~ 從字符串解析一個 LocalDate 類型和解析 LocalTime 一樣簡單,下面是使用`DateTimeFormatter`解析字符串的例子: ~~~java String str1 = "2014==04==12 01時06分09秒"; // 根據需要解析的日期、時間字符串定義解析所用的格式器 DateTimeFormatter fomatter1 = DateTimeFormatter .ofPattern("yyyy==MM==dd HH時mm分ss秒"); LocalDateTime dt1 = LocalDateTime.parse(str1, fomatter1); System.out.println(dt1); // 輸出 2014-04-12T01:06:09 String str2 = "2014$$$四月$$$13 20小時"; DateTimeFormatter fomatter2 = DateTimeFormatter .ofPattern("yyy$$$MMM$$$dd HH小時"); LocalDateTime dt2 = LocalDateTime.parse(str2, fomatter2); System.out.println(dt2); // 輸出 2014-04-13T20:00 ~~~ 再來看一個使用`DateTimeFormatter`格式化日期的示例 ~~~java LocalDateTime rightNow=LocalDateTime.now(); String date=DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(rightNow); System.out.println(date);//2019-03-12T16:26:48.29 DateTimeFormatter formatter=DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss"); System.out.println(formatter.format(rightNow));//2019-03-12 16:26:48 ~~~ ### LocalDateTime(本地日期時間) LocalDateTime 同時表示了時間和日期,相當于前兩節內容合并到一個對象上了。LocalDateTime 和 LocalTime還有 LocalDate 一樣,都是不可變的。LocalDateTime 提供了一些能訪問具體字段的方法。 ~~~java 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 ~~~ 只要附加上時區信息,就可以將其轉換為一個時間點Instant對象,Instant時間點對象可以很容易的轉換為老式的`java.util.Date`。 ~~~java Instant instant = sylvester .atZone(ZoneId.systemDefault()) .toInstant(); Date legacyDate = Date.from(instant); System.out.println(legacyDate); // Wed Dec 31 23:59:59 CET 2014 ~~~ 格式化LocalDateTime和格式化時間和日期一樣的,除了使用預定義好的格式外,我們也可以自己定義格式: ~~~java 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是不可變的,所以它是線程安全的。 關于時間日期格式的詳細信息在[這里](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html)。 ## Annotations(注解) 在Java 8中支持多重注解了,先看個例子來理解一下是什么意思。 首先定義一個包裝類Hints注解用來放置一組具體的Hint注解: ~~~java @interface Hints { Hint[] value(); } @Repeatable(Hints.class) @interface Hint { String value(); } ~~~ Java 8允許我們把同一個類型的注解使用多次,只需要給該注解標注一下`@Repeatable`即可。 例 1: 使用包裝類當容器來存多個注解(老方法) ~~~java @Hints({@Hint("hint1"), @Hint("hint2")}) class Person {} ~~~ 例 2:使用多重注解(新方法) ~~~java @Hint("hint1") @Hint("hint2") class Person {} ~~~ 第二個例子里java編譯器會隱性的幫你定義好@Hints注解,了解這一點有助于你用反射來獲取這些信息: ~~~java 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 ~~~ 即便我們沒有在`Person`類上定義`@Hints`注解,我們還是可以通過`getAnnotation(Hints.class)`來獲取`@Hints`注解,更加方便的方法是使用`getAnnotationsByType`可以直接獲取到所有的`@Hint`注解。 另外Java 8的注解還增加到兩種新的target上了: ~~~java @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) @interface MyAnnotation {} ~~~ ## Whete to go from here? 關于Java 8的新特性就寫到這了,肯定還有更多的特性等待發掘。JDK 1.8里還有很多很有用的東西,比如`Arrays.parallelSort`,`StampedLock`和`CompletableFuture`等等。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看