JAVA 8 新特性知識(shí)總結(jié)

JAVA 8 新特性

Java 8 應(yīng)該是目前項(xiàng)目中使用最多的版本琼讽,之前有使用過它的一些新特性,了解一些基本的用法哗咆,但是對(duì)于一些理論性的概念不是很清楚呻右,最近看了一些教程和博客,收獲很大,在這里記錄一下肌割。

介紹

Java 8 新增了非常多的新特性,包括一些數(shù)據(jù)結(jié)構(gòu)的優(yōu)化帐要,JVM的優(yōu)化把敞,這里只記錄一些日常中用到的新特性:

  1. Lambda表達(dá)式
  2. 方法引用
  3. 函數(shù)式接口
  4. 默認(rèn)方法
  5. Stream
  6. Optional
  7. 新的日期API

1. Lambda

Lambda 允許把函數(shù)作為一個(gè)方法的參數(shù)(函數(shù)作為參數(shù)傳遞進(jìn)方法中)。
使用 Lambda 表達(dá)式可以使代碼變的更加簡(jiǎn)潔緊湊榨惠。

  • 先來感受一下從 匿名內(nèi)部類 到 Lambda 的轉(zhuǎn)換

    • 案例一

      //匿名內(nèi)部類
      Runnable r = new Runnable() {
          @Override
          public void run() {
              System.out.println("Hello World!");
          }
      };
      
      //Lambda 表達(dá)式
      Runnable r2 = () -> System.out.println("Hello World!");
      
      r.run();
      r2.run();
      
    • 案例二

      //使用匿名內(nèi)部類作為參數(shù)傳遞
      TreeSet<String> t = new TreeSet<>(new Comparator<String>() {
          @Override
          public int compare(String o1, String o2) {
              return Integer.compare(o1.length(),o2.length());
          }
      });
      
      //Lambda 表達(dá)式作為參數(shù)傳遞
      TreeSet<String> t2 = new TreeSet<>((o1, o2) -> Integer.compare(o1.length(), o2.length()));
      

      通過上面兩個(gè)案例可以看出奋早,Lambda 是一個(gè)匿名函數(shù),我們可以把 Lambda 表達(dá)式理解為是一段可以傳遞的代碼(將代碼像數(shù)據(jù)一樣進(jìn)行傳遞)赠橙。Lambda 明顯的減少了代碼量耽装。

  • Lambda表達(dá)式語法

    • 語法格式
    //引入了新的語法元素和操作符,"->" 箭頭操作符將表達(dá)式分成了兩部分
    //左邊 表達(dá)式所需要的參數(shù) 右邊 表達(dá)式將要執(zhí)行的功能
    (parameters) -> expression
    (parameters) -> { statements;}
    
    • 語法特性

      • 可選類型聲明:不需要聲明參數(shù)類型期揪,編譯器可以統(tǒng)一識(shí)別參數(shù)值掉奄。
      • 可選的參數(shù)圓括號(hào):一個(gè)參數(shù)無需定義圓括號(hào),但多個(gè)參數(shù)需要定義圓括號(hào)凤薛。
      • 可選的大括號(hào):如果主體包含了一個(gè)語句姓建,就不需要使用大括號(hào)。
      • 可選的返回關(guān)鍵字:如果主體只有一個(gè)表達(dá)式返回值則編譯器會(huì)自動(dòng)返回值缤苫,使用大括號(hào)時(shí)需要指明表達(dá)式return的值速兔。

2. 函數(shù)式接口

就是一個(gè)有且僅有一個(gè)抽象方法,但是可以有多個(gè)非抽象方法的接口活玲。

  • 自定義函數(shù)式接口
@FunctionalInterface
public interface MyShow {
    public String getShow();
}

//函數(shù)式接口中使用泛型
@FunctionalInterface
public interface MyShow02<T> {
    public T getShow(T t);
}
  • Lambda 作為參數(shù)傳遞時(shí)涣狗,接收的參數(shù)類型必須是與 Lambda 兼容的函數(shù)式接口
public static String myShow02(MyShow02<String> myShow02,String s){
        return myShow02.getShow(s);
}

//Lambda作為參數(shù)傳遞
String afssd = myShow02(a -> a.toUpperCase(), "afssd");
System.out.println(afssd);
  • Java 內(nèi)置的四大核心函數(shù)式接口

    函數(shù)式接口 參數(shù)類型 返回類型 用途
    Consumer<T>
    消費(fèi)型接口
    T void 對(duì)類型為T的對(duì)象應(yīng)用操作。
    包含方法: void accept(T t);
    Supplier<T>
    供給型接口
    T 返回類型為T的對(duì)象舒憾。
    包含方法:T get();
    Function<T镀钓,R>
    函數(shù)型接口
    T R 對(duì)類型為T的對(duì)象應(yīng)用操作,并返回結(jié)果珍剑。結(jié)果是R類型的對(duì)象掸宛。
    包含方法:R apply(T t);
    Predicate<T>
    斷定型接口
    T boolean 確定類型為T的對(duì)象是否滿足某約束,并返回boolean 值招拙。
    包含方法boolean test(T t);
  • 其它接口

    函數(shù)式接口 參數(shù)類型 返回類型 用途
    BiFunction<T, U, R> T, U R 對(duì)類型為 T, U 參數(shù)應(yīng)用操作唧瘾,返回 R 類型的結(jié)果。
    包含方法為:R apply(T t, U u);
    UnaryOperator<T>
    (Function子接口)
    T T 對(duì)類型為T的對(duì)象進(jìn)行一元運(yùn)算别凤,并返回T類型的結(jié)果饰序。
    包含方法為T apply(T t);
    BinaryOperator<T>
    (BiFunction 子接口)
    T T 對(duì)類型為T的對(duì)象進(jìn)行二元運(yùn)算,并返回T類型的結(jié)果规哪。
    包含方法為T apply(T t1, T t2);
    BiConsumer<T, U> T, U void 對(duì)類型為T, U 參數(shù)應(yīng)用操作求豫。包含方法為void accept(T t, U u);
    IntFunction<R>
    LongFunction<R>
    DoubleFunction<R>
    T int
    long
    double
    分 別 計(jì) 算 int 、 long 蝠嘉、double值的函數(shù)
    IntFunction<R>
    LongFunction<R>
    DoubleFunction<R>
    int
    long
    double
    R R 參數(shù)分別為int最疆、long、double 類型的函數(shù)

3. 方法引用

當(dāng)要傳遞給Lambda體的操作蚤告,已經(jīng)有實(shí)現(xiàn)的方法了努酸,可以使用方法引用!

方法引用與Lambda結(jié)合使用可以使代碼更加簡(jiǎn)潔杜恰,緊湊获诈。

  • 語法

    • 使用操作符" :: ",將方法名和對(duì)象或類的名字分隔開來心褐。
  • 使用場(chǎng)景

    • Lambda表達(dá)式的主體僅包含一個(gè)表達(dá)式舔涎,且該表達(dá)式僅調(diào)用一個(gè)已經(jīng)存在的方法
    • 方法引用所使用的方法的入?yún)⒑头祷刂蹬cLambda表達(dá)式實(shí)現(xiàn)的函數(shù)式接口的入?yún)⒑头祷刂狄恢?/strong>
  • 方法引用

    • 方法引用的四種形式

      1. 靜態(tài)方法引用       :       ClassName :: staticMethodName
      2. 構(gòu)造器引用       《旱:       ClassName :: new
      3. 特定類的任意對(duì)象的實(shí)例方法引用:    ClassName :: instanceMethodName
      4. 特定對(duì)象的實(shí)例方法引用 ⊥鱿印:       object :: instanceMethodName
      
    • 案例

      • 靜態(tài)方法引用

        Lambda表達(dá)式主體僅調(diào)用某個(gè)類的靜態(tài)方法時(shí)使用。

        class Java8Test03 {
            public static void main(String args[]) {
                //使用Lambda表達(dá)式
                Arrays.asList("張三1", "李四1", "王二1").stream().forEach((x) -> Java8Test03.show(x));
                //使用靜態(tài)方法引用
                Arrays.asList("張三2", "李四2", "王二2").stream().forEach(Java8Test03::show);
            }
          //靜態(tài)方法
            public static void show(String name) {
                System.out.println(name + "show!");
            }
        }
        
        張三1show!
        李四1show!
        王二1show!
        張三2show!
        李四2show!
        王二2show!
        
      • 構(gòu)造器引用

        Lambda表達(dá)式主體僅調(diào)用某個(gè)類構(gòu)造方法返回實(shí)例時(shí)使用桶至。

        class Java8Test04 {
            public static void main(String args[]) {
                //使用Lambda表達(dá)式
                Function<Integer, Integer[]> fun = (n) -> new Integer[n];
                //使用構(gòu)造器引用
                Function<Integer, Integer[]> fun2 = Integer[]::new;
            }
        }
        
      • 特定類的任意對(duì)象的實(shí)例方法引用

        1. 方法引用所使用方法的入?yún)⒑头祷刂狄cLambda表達(dá)式實(shí)現(xiàn)的函數(shù)式接口的入?yún)⒑头祷刂狄恢隆?/strong>
        2. Lambda表達(dá)式的第一個(gè)入?yún)閷?shí)例方法的調(diào)用者昼伴,后面的入?yún)⑴c實(shí)例方法一致。
        class MyUser {
        
            private String name;
            private Integer age;
        
            public void setNameAndAge(String name, Integer age) {
                this.name = name;
                this.age = age;
                System.out.println("name: " + name + "age: " + age);
            }
        
            public static void main(String args[]) {
                //lambda表達(dá)式的用法:
                TestInterface testInterface1 = (myUser, name, age) -> myUser.setNameAndAge(name, age);
                testInterface1.set(new MyUser(), "張三", 18);
                //類的任意對(duì)象的實(shí)例方法引用的用法:
                TestInterface testInterface2 = MyUser::setNameAndAge;
                testInterface2.set(new MyUser(), "李四", 25);
            }
        
            @FunctionalInterface
            interface TestInterface {
                // 注意:入?yún)⒈萓ser類的setNameAndAge方法多1個(gè)MyUser對(duì)象镣屹,除第一個(gè)外其它入?yún)㈩愋鸵恢?        public void set(MyUser myUser, String name, Integer age);
            }
        }
        
        name: 張三age: 18
        name: 李四age: 25
        
      • 特定對(duì)象的實(shí)例方法引用

        Lambda表達(dá)式主體中僅調(diào)用了某個(gè)對(duì)象的某個(gè)實(shí)例方法時(shí)使用圃郊。

        class Java8Test05 {
            public static void main(String args[]) {
                //使用Lambda表達(dá)式
                Arrays.asList("張三1", "李四1", "王二1").stream().forEach((x) -> Java8Test03.show(x));
                //使用靜態(tài)方法引用
                Arrays.asList("張三2", "李四2", "王二2").stream().forEach(Java8Test03::show);
            }
          //實(shí)例方法
            public void show(String name) {
                System.out.println(name + "show!");
            }
        }
        
        張三1show!
        李四1show!
        王二1show!
        張三2show!
        李四2show!
        王二2show!
        

4. 默認(rèn)方法

默認(rèn)方法就是接口可以有實(shí)現(xiàn)方法,而且不需要實(shí)現(xiàn)類去實(shí)現(xiàn)這個(gè)方法女蜈。

我們只需要在方法名前加default關(guān)鍵字即可標(biāo)記該方法為默認(rèn)方法持舆。

  • 為什么要有這個(gè)特性?
首先伪窖,之前的接口是個(gè)雙刃劍逸寓,
好處是面向抽象而不是面向具體編程,
缺陷是覆山,當(dāng)需要修改接口時(shí)候竹伸,需要修改全部實(shí)現(xiàn)該接口的類,目前的 java 8 之前的集合框架沒有 foreach 方法簇宽,
通常能想到的解決辦法是在JDK里給相關(guān)的接口添加新的方法及實(shí)現(xiàn)勋篓。
然而,對(duì)于已經(jīng)發(fā)布的版本魏割,是沒法在給接口添加新方法的同時(shí)不影響已有的實(shí)現(xiàn)譬嚣。所以引進(jìn)的默認(rèn)方法。
他們的目的是為了解決接口的修改與現(xiàn)有的實(shí)現(xiàn)不兼容的問題钞它。
  • 語法

    public interface TestInterface {
        default void print() {
            System.out.println("我是默認(rèn)方法拜银。");
        }
    }
    
  • 多個(gè)默認(rèn)方法

    我們知道一個(gè)類可以實(shí)現(xiàn)多個(gè)接口殊鞭,那么如果恰巧這些接口擁有相同的默認(rèn)方法,實(shí)現(xiàn)類在調(diào)用默認(rèn)方法時(shí)尼桶,會(huì)選擇使用哪個(gè)接口的默認(rèn)方法呢操灿?

    為了解決這種情況的沖突,Java 8 提供了以下三條原則:

    1. 類中的方法優(yōu)先級(jí)最高泵督,類或父類中申明的方法優(yōu)先級(jí)高于任何聲明為默認(rèn)方法的優(yōu)先級(jí)牲尺。
    2. 如果第一條無法判斷,那么子接口的優(yōu)先級(jí)更高:方法簽名相同時(shí)幌蚊,優(yōu)先選擇擁有最具實(shí)現(xiàn)的默認(rèn)方法的接口,即B繼承了A溃卡,那么B比A更具體溢豆。
    3. 最后,如果還是無法判斷瘸羡,繼承了多個(gè)接口的類必須通過顯式覆蓋和調(diào)用期望的方法漩仙, 顯式地選擇使用哪一個(gè)默認(rèn)方法的實(shí)現(xiàn)。

5. Stream

是數(shù)據(jù)渠道犹赖,用于操作數(shù)據(jù)源(集合队他、數(shù)組等)所生成的元素序列。

集合注重?cái)?shù)據(jù)存儲(chǔ)峻村,流注重計(jì)算麸折!

  • 注意

    1. Stream 自己不會(huì)存儲(chǔ)元素。
    2. Stream 不會(huì)改變?cè)磳?duì)象粘昨。相反垢啼,他們會(huì)返回一個(gè)持有結(jié)果的新Stream。
    3. Stream 操作是延遲執(zhí)行的张肾。這意味著他們會(huì)等到需要結(jié)果的時(shí)候才執(zhí)行芭析。
  • Stream操作的三個(gè)步驟

    • 創(chuàng)建 Stream

      一個(gè)數(shù)據(jù)源(如:集合、數(shù)組)吞瞪,獲取一個(gè)流馁启。以下是獲取流的三種方式:

      • Java 8中的Collection接口被擴(kuò)展,提供了兩個(gè)獲取流的方法

        //返回一個(gè)順序流
        default Stream<E> stream() {
            return StreamSupport.stream(spliterator(), false);
        }
        //返回一個(gè)并行流 (并行流就是把一個(gè)內(nèi)容分成多個(gè)數(shù)據(jù)塊芍秆,并用不同的線程分別處理每個(gè)數(shù)據(jù)塊的流惯疙。)
        default Stream<E> parallelStream() {
            return StreamSupport.stream(spliterator(), true);
        }
        
      • Arrays 的靜態(tài)方法 stream() 可以獲取數(shù)組流

        //返回一個(gè)流
        public static <T> Stream<T> stream(T[] array) {
            return stream(array, 0, array.length);
        }
        
        
        //重載形式,能夠處理對(duì)應(yīng)基本類型的數(shù)組:
        public static IntStream stream(int[] array) {
            return stream(array, 0, array.length);
        }
        public static IntStream stream(int[] array, int startInclusive, int endExclusive) {
            return StreamSupport.intStream(spliterator(array, startInclusive, endExclusive), false);
        }
        public static LongStream stream(long[] array) {
            return stream(array, 0, array.length);
        }
        public static LongStream stream(long[] array, int startInclusive, int endExclusive) {
            return StreamSupport.longStream(spliterator(array, startInclusive, endExclusive), false);
        }
        public static DoubleStream stream(double[] array) {
            return stream(array, 0, array.length);
        }
        public static DoubleStream stream(double[] array, int startInclusive, int endExclusive) {
            return StreamSupport.doubleStream(spliterator(array, startInclusive, endExclusive), false);
        }
        
        
      • 由值創(chuàng)建流

        //可以使用靜態(tài)方法 Stream.of(), 通過顯示值創(chuàng)建一個(gè)流浪听。它可以接收任意數(shù)量的參數(shù)螟碎。
        public static<T> Stream<T> of(T t) {
            return StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);
        }
        
      • 由函數(shù)創(chuàng)建流:創(chuàng)建無限流

        //迭代
        public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {...}
        //生成
        public static<T> Stream<T> generate(Supplier<T> s) {...}
        
        
    • 中間操作

      一個(gè)中間操作鏈,對(duì)數(shù)據(jù)源的數(shù)據(jù)進(jìn)行處理

      多個(gè)中間操作可以連接起來形成一個(gè)流水線迹栓,除非流水線上觸發(fā)終止操作掉分,否則中間操作不會(huì)執(zhí)行任何的處理俭缓!而在終止操作時(shí)一次性全部處理,稱為“惰性求值”

      • 篩選與切片

        方法 描述
        filter(Predicate p) 接收 Lambda 酥郭, 從流中排除某些元素华坦。
        distinct() 篩選,通過流所生成元素的 hashCode() 和 equals() 去除重復(fù)元素
        limit(long maxSize) 截?cái)嗔鞑淮樱蛊湓夭怀^給定數(shù)量
        skip(long n) 跳過元素惜姐,返回一個(gè)扔掉了前 n 個(gè)元素的流。若流中元素不足 n 個(gè)椿息,則返回一個(gè)空流歹袁。與 limit(n) 互補(bǔ)
      • 映射

        方法 描述
        map(Function f) 接收一個(gè)函數(shù)作為參數(shù),該函數(shù)會(huì)被應(yīng)用到每個(gè)元素上寝优,并將其映射成一個(gè)新的元素条舔。
        mapToDouble(ToDoubleFunction f) 接收一個(gè)函數(shù)作為參數(shù),該函數(shù)會(huì)被應(yīng)用到每個(gè)元素上乏矾,產(chǎn)生一個(gè)新的 DoubleStream孟抗。
        mapToInt(ToIntFunction f) 接收一個(gè)函數(shù)作為參數(shù),該函數(shù)會(huì)被應(yīng)用到每個(gè)元素上钻心,產(chǎn)生一個(gè)新的 IntStream凄硼。
        mapToLong(ToLongFunction f) 接收一個(gè)函數(shù)作為參數(shù),該函數(shù)會(huì)被應(yīng)用到每個(gè)元
        flatMap(Function f) 接收一個(gè)函數(shù)作為參數(shù)捷沸,將流中的每個(gè)值都換成另一個(gè)流摊沉,然后把所有流連接成一個(gè)流
      • 排序

        方法 描述
        sorted() 產(chǎn)生一個(gè)新流,其中按自然順序排序
        sorted(Comparator comp) 產(chǎn)生一個(gè)新流亿胸,其中按比較器順序排序
  • 中止操作(終端操作)

    一個(gè)終止操作坯钦,執(zhí)行中間操作鏈,并產(chǎn)生結(jié)果

    終端操作會(huì)從流的流水線生成結(jié)果侈玄。其結(jié)果可以是任何不是流的值婉刀,例如:List、Integer序仙,甚至是 void 突颊。

    • 查找與匹配

      方法 描述
      allMatch(Predicate p) 檢查是否匹配所有元素
      anyMatch(Predicate p) 檢查是否至少匹配一個(gè)元素
      noneMatch(Predicate p) 檢查是否沒有匹配所有元素
      findFirst() 返回第一個(gè)元素
      findAny() 返回當(dāng)前流中的任意元素
      count() 返回流中元素總數(shù)
      max(Comparator c) 返回流中最大值
      min(Comparator c) 返回流中最小值
      forEach(Consumer c) 內(nèi)部迭代
      (使用 Collection 接口需要用戶去做迭代,稱為外部迭代 )
    • 歸約

      方法 描述
      reduce(T iden, BinaryOperator b) 可以將流中元素反復(fù)結(jié)合起來潘悼,得到一個(gè)值律秃。返回 T
      reduce(BinaryOperator b) 可以將流中元素反復(fù)結(jié)合起來,得到一個(gè)值治唤。返回 Optional<T>
    • 收集

      方法 描述
      collect(Collector c) 將流轉(zhuǎn)換為其他形式棒动。接收一個(gè) Collector接口的實(shí)現(xiàn),用于給Stream中元素做匯總的方法

Collector 接口中方法的實(shí)現(xiàn)決定了如何對(duì)流執(zhí)行收集操作(如收集到 List宾添、Set船惨、Map)柜裸。但是 Collectors 實(shí)用類提供了很多靜態(tài)方法,可以方便地創(chuàng)建常見收集器實(shí)例粱锐,具體方法與實(shí)例如下表:

方法 返回類型 作用 示例
toList List<T> 把流中元素收集到List List<Employee> emps= list.stream().collect(Collectors.toList());
toSet Set<T> 把流中元素收集到Set Set<Employee> emps= list.stream().collect(Collectors.toSet());
toCollection Collection<T> 把流中元素收集到創(chuàng)建的集合 Collection<Employee>emps=list.stream().collect(Collectors.toCollection(ArrayList::new));
counting Long 計(jì)算流中元素的個(gè)數(shù) long count = list.stream().collect(Collectors.counting());
summingInt Integer 對(duì)流中元素的整數(shù)屬性求和 inttotal=list.stream().collect(Collectors.summingInt(Employee::getSalary));
averagingInt Double 計(jì)算流中元素Integer屬性的平均值 doubleavg= list.stream().collect(Collectors.averagingInt(Employee::getSalary));
summarizingInt IntSummaryStatistics 收集流中Integer屬性的統(tǒng)計(jì)值疙挺。如:平均值 IntSummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Employee::getSalary));
joining String 連接流中每個(gè)字符串 String str= list.stream().map(Employee::getName).collect(Collectors.joining());
maxBy Optional<T> 根據(jù)比較器選擇最大值 Optional<Emp>max= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)));
minBy Optional<T> 根據(jù)比較器選擇最小值 Optional<Emp> min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary)));
reducing 歸約產(chǎn)生的類型 從一個(gè)作為累加器的初始值開始,利用BinaryOperator與流中元素逐個(gè)結(jié)合怜浅,從而歸約成單個(gè)值 inttotal=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum));
collectingAndThen 轉(zhuǎn)換函數(shù)返回的類型 包裹另一個(gè)收集器铐然,對(duì)其結(jié)果轉(zhuǎn)換函數(shù) inthow= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
groupingBy Map<K, List<T>> 根據(jù)某屬性值對(duì)流分組,屬性為K恶座,結(jié)果為V Map<Emp.Status, List<Emp>> map= list.stream().collect(Collectors.groupingBy(Employee::getStatus));
partitioningBy Map<Boolean, List<T>> 根據(jù)true或false進(jìn)行分區(qū) Map<Boolean,List<Emp>>vd= list.stream().collect(Collectors.partitioningBy(Employee::getManage));

6. Optional

`Optional<T> 類(java.util.Optional) 是一個(gè)容器類搀暑,代表一個(gè)值存在或不存在,原來用 null 表示一個(gè)值不存在跨琳,現(xiàn)在 Optional 可以更好的表達(dá)這個(gè)概念险掀。并且可以避免空指針異常。

  • 常用方法

    Optional.of(T t) : 創(chuàng)建一個(gè) Optional 實(shí)例
    Optional.empty() : 創(chuàng)建一個(gè)空的 Optional 實(shí)例
    Optional.ofNullable(T t):若 t 不為 null,創(chuàng)建 Optional 實(shí)例,否則創(chuàng)建空實(shí)例
    isPresent() : 判斷是否包含值
    orElse(T t) : 如果調(diào)用對(duì)象包含值湾宙,返回該值,否則返回t
    orElseGet(Supplier s) :如果調(diào)用對(duì)象包含值冈绊,返回該值侠鳄,否則返回 s 獲取的值
    map(Function f): 如果有值對(duì)其處理,并返回處理后的Optional死宣,否則返回 Optional.empty()
    flatMap(Function mapper):與 map 類似伟恶,要求返回值必須是Optional
    

7. Date Time API

Date Time API (JSR 310)進(jìn)一步加強(qiáng)對(duì)日期與時(shí)間的處理

  • 為什么要發(fā)布新的 Date Time API ?

    舊版Java中,日期時(shí)間API存在諸多問題毅该,其中有:

    • 非線程安全 - java.util.Date 是非線程安全的博秫,所有的日期類都是可變的,這是Java日期類最大的問題之一眶掌。
    • 設(shè)計(jì)很差 ? Java的日期/時(shí)間類的定義并不一致挡育,在java.utiljava.sql的包中都有日期類,此外用于格式化和解析的類在java.text包中定義朴爬。java.util.Date同時(shí)包含日期和時(shí)間即寒,而java.sql.Date僅包含日期,將其納入java.sql包并不合理召噩。另外這兩個(gè)類都有相同的名字母赵,這本身就是一個(gè)非常糟糕的設(shè)計(jì)。
    • 時(shí)區(qū)處理麻煩 ? 日期類并不提供國際化具滴,沒有時(shí)區(qū)支持凹嘲,因此Java引入了java.util.Calendarjava.util.TimeZone類,但他們同樣存在上述所有的問題构韵。

    新的java.time包涵蓋了所有處理日期周蹭,時(shí)間趋艘,日期/時(shí)間,時(shí)區(qū)谷醉,時(shí)刻(instants)致稀,過程(during)與時(shí)鐘(clock)的操作。以下為兩個(gè)比較重要的 API:

    • Local(本地) ? 簡(jiǎn)化了日期時(shí)間的處理俱尼,沒有時(shí)區(qū)的問題抖单。
    • Zoned(時(shí)區(qū)) ? 通過制定的時(shí)區(qū)處理日期時(shí)間。
  • LocalDate遇八、LocalTime矛绘、LocalDateTime

    LocalDateLocalTime刃永、LocalDateTime類的實(shí)例是不可變的對(duì)象货矮,分別表示使用 ISO-8601日歷系統(tǒng)的日期、時(shí)間斯够、日期和時(shí)間囚玫。它們提供了簡(jiǎn)單的日期或時(shí)間,并不包含當(dāng)前的時(shí)間信息读规。也不包含與時(shí)區(qū)相關(guān)的信息抓督。

    public static void main(String[] args) {
            /**
             * LocalDate
             *
             * getYear()        當(dāng)前日期年份信息
             * getMonth()       當(dāng)前日期月份信息
             * getDayOfMonth()  當(dāng)前日期是一個(gè)月中的第幾天
             * getDayOfWeek()   當(dāng)前日期是周幾
             * lengthOfMonth()  當(dāng)前月有多少天
             * isLeapYear       是否是閏年
             */
            System.out.println("*******************LocalDate*******************");
            LocalDate localDate = LocalDate.of(2020, 9, 1);
            System.out.println(localDate.getYear() + "\t" + 
                               localDate.getMonth() + "\t" + 
                               localDate.getDayOfMonth() + "\t" + 
                               localDate.getDayOfWeek() + "\t" + 
                               localDate.lengthOfMonth() + "\t" + 
                               localDate.isLeapYear());
            LocalDate now = LocalDate.now();
            System.out.println(now.get(ChronoField.YEAR) + "\t" + 
                               now.get(ChronoField.MONTH_OF_YEAR) + "\t" +
                               now.get(ChronoField.DAY_OF_MONTH));
    
            /**
             * LocalTime
             *
             * getHour      時(shí)
             * getMinute    分
             * getSecond    秒
             */
            System.out.println("*******************LocalTime*******************");
            LocalTime localTime = LocalTime.of(20, 44, 12);
            System.out.println(localTime.getHour() + "\t" + 
                               localTime.getMinute() + "\t" + 
                               localTime.getSecond());
    
            /**
             * 解析字符串
             * 默認(rèn)格式: yyyy-MM-dd
             */
            System.out.println("*******************解析字符串*******************");
            LocalDate localDate2 = LocalDate.parse("2020-09-01");
            System.out.println(localDate2.toString());
    
            /**
             * 解析字符串
             * 默認(rèn)格式: HH:mm:ss.SSS
             */
            LocalTime localTime2 = LocalTime.parse("20:42:12.828");
            System.out.println(localTime2.toString());
    
            /**
             * 互相進(jìn)行類型轉(zhuǎn)換
             */
            System.out.println("*******************互相進(jìn)行類型轉(zhuǎn)換*******************");
            
            LocalDateTime localDateTime1 = LocalDateTime
                                          .of(2020, 9, 1, 16, 12, 10, 888)
                                          .atZone(ZoneId.of("Asia/Shanghai"))
                                          .toLocalDateTime();
            System.out.println(localDateTime1);
            
            //LocalDate + LocalTime -> LocalDateTime
            LocalDateTime localDateTime2 = LocalDateTime.of(localDate2, localTime2);
            System.out.println(localDateTime2);
            
            //組合拼接
            LocalDateTime localDateTime3 = localDate2.atTime(10, 10, 10);
            System.out.println(localDateTime3);
            
            LocalDateTime localDateTime4 = localDate2.atTime(localTime2);
            System.out.println(localDateTime4);
            
            LocalDateTime localDateTime5 = localTime2.atDate(localDate2);
            System.out.println(localDateTime5);
            
            LocalDateTime localDateTime6 = 
                LocalDateTime.parse("2020/09/01 16:19:20.888", DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.SSS"));
            System.out.println(localDateTime6);
            LocalDate localDate3 = localDateTime6.toLocalDate();
            System.out.println(localDate3);
            LocalTime toLocalTime3 = localDateTime6.toLocalTime();
            System.out.println(toLocalTime3);
        }
    
    ******************* LocalDate *******************
    2020  SEPTEMBER   1   TUESDAY 30  true
    2020  9   2
    ******************* LocalTime *******************
    20    44  12
    ******************* 解析字符串 *******************
    2020-09-01
    20:42:12.828
    ******************* 互相進(jìn)行類型轉(zhuǎn)換 *******************
    2020-09-01T16:12:10.000000888
    2020-09-01T20:42:12.828
    2020-09-01T10:10:10
    2020-09-01T20:42:12.828
    2020-09-01T20:42:12.828
    2020-09-01T16:19:20.888
    2020-09-01
    16:19:20.888
    
    
  • Instatnt

    Instant對(duì)時(shí)間的建模方式是以UTC時(shí)區(qū)的1970年1月1日午夜時(shí)分開始所經(jīng)歷的秒數(shù)進(jìn)行計(jì)算佃乘,它不包含時(shí)區(qū)信息阔墩。

     public static void main(String[] args) {
            //獲取當(dāng)前時(shí)間戳
            long milli = Instant.now().toEpochMilli();
            System.out.println("milli:" + milli);
            //根據(jù)某個(gè)時(shí)間戳獲取Instant實(shí)例
            Instant instant = Instant.ofEpochMilli(milli);
            System.out.println("instant:" + instant);
    
            //minusSeconds() 減一秒 
            Instant instant2 = instant.minusSeconds(1L);
            System.out.println("instant2:" + instant2);
    
            //isBefore()和isAfter()比較大小
            System.out.println(instant.isAfter(instant2));
        }
    
  • Duration和Period

    Duration 用于計(jì)算兩個(gè)“時(shí)間”間隔锚贱, Period用于計(jì)算兩個(gè)“日期”間隔

     public static void main(String[] args) {
            Duration d1 = Duration.between(LocalDateTime.of(2020, 9, 1, 15, 55, 55, 888), LocalDateTime.now());
            Duration d2 = Duration.between(LocalTime.of(17, 55, 10), LocalTime.now());
            Duration d3 = Duration.between(Instant.ofEpochMilli(1599037854143L), Instant.now());
            System.out.println(d3.toMinutes());
    
            //Duration對(duì)象用秒和納秒來衡量時(shí)間的長(zhǎng)短冰垄,所以入?yún)⒉荒苁褂肔ocalDate類型, 否則拋UnsupportedTemporalTypeException: Unsupported unit: Seconds
            //Duration.between(LocalDate.of(2019, 10, 7), LocalDate.now());
    
            //如果想要對(duì)多個(gè)時(shí)間對(duì)象進(jìn)行日期運(yùn)算讲逛,可以用Period
            Period p1 = Period.between(LocalDate.of(2019, 1, 1), LocalDate.now());
            System.out.println(p1.getYears() + "\t" + p1.getMonths() + "\t" + p1.getDays());
    
        }
    
  • TemporalAdjuster

    TemporalAdjuster 時(shí)間校正器场晶。有時(shí)我們可能需要獲取例如:將日期調(diào)整到“下個(gè)周日”等操作矮锈。

    TemporalAdjusters : 該類通過靜態(tài)方法提供了大量的常用 TemporalAdjuster 的實(shí)現(xiàn)繁疤。

    public static void main(String[] args) {
            LocalDateTime localDateTime = LocalDateTime.now();
            // 本年本月最后一天
            System.out.println(localDateTime.with(TemporalAdjusters.lastDayOfMonth()));
            // 本年本月第一天
            System.out.println(localDateTime.with(TemporalAdjusters.firstDayOfMonth()));
            // 本年下一月第一天
            System.out.println(localDateTime.with(TemporalAdjusters.firstDayOfNextMonth()));
            // 下一年第一天
            System.out.println(localDateTime.with(TemporalAdjusters.firstDayOfNextYear()));
            // 本年最后一天
            System.out.println(localDateTime.with(TemporalAdjusters.lastDayOfYear()));
            // 下一個(gè)周五
            System.out.println(localDateTime.with(TemporalAdjusters.next(DayOfWeek.FRIDAY)));
            // 本月第一個(gè)周五
            System.out.println(localDateTime.with(TemporalAdjusters.firstInMonth(DayOfWeek.FRIDAY)));
            // 本月最后一個(gè)周五
            System.out.println(localDateTime.with(TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY)));
            // 下一個(gè)周五怕敬,如果當(dāng)前是周五則返回當(dāng)前時(shí)間
            System.out.println(localDateTime.with(TemporalAdjusters.nextOrSame(DayOfWeek.FRIDAY)));
            // 前一個(gè)周五
            System.out.println(localDateTime.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY)));
            // 前一個(gè)周五揣炕,如果當(dāng)前是周五則返回當(dāng)前時(shí)間
            System.out.println(localDateTime.with(TemporalAdjusters.previousOrSame(DayOfWeek.FRIDAY)));
        }
    
    2020-09-30T17:45:14.274
    2020-09-01T17:45:14.274
    2020-10-01T17:45:14.274
    2021-01-01T17:45:14.274
    2020-12-31T17:45:14.274
    2020-09-04T17:45:14.274
    2020-09-04T17:45:14.274
    2020-09-25T17:45:14.274
    2020-09-04T17:45:14.274
    2020-08-28T17:45:14.274
    2020-08-28T17:45:14.274
    
  • DateTimeFormatter

    java.time.format.DateTimeFormatter 類,該類提供了三種格式化方法:

    • 預(yù)定義的標(biāo)準(zhǔn)格式

    • 語言環(huán)境相關(guān)的格式

    • 自定義的格式

    public static void main(String[] args) {
            //日期轉(zhuǎn)字符串
            LocalDate ld = LocalDate.of(2020, 9, 1);
            String s1 = ld.format(DateTimeFormatter.BASIC_ISO_DATE);
            System.out.println(s1);
            String s2 = ld.format(DateTimeFormatter.ISO_LOCAL_DATE);
            System.out.println(s2);
            //字符串轉(zhuǎn)日期
            LocalDateTime ld1 = LocalDateTime.parse("2020-09-01 18:00:00.888",                                                    DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
            System.out.println(ld1);
        }
    
    20200901
    2020-09-01
    2020-09-01T18:00:00.888
    
  • 時(shí)區(qū)的處理

    Java8 中加入了對(duì)時(shí)區(qū)的支持,帶時(shí)區(qū)的時(shí)間為分別為:ZonedDate东跪、ZonedTime祝沸、ZonedDateTime

    其中每個(gè)時(shí)區(qū)都對(duì)應(yīng)著 ID,地區(qū)ID都為 “{區(qū)域}/{城市}”的格式

    public static void main(String[] args) {
            //獲取所有合法的“區(qū)域/城市”字符串
            Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
            //availableZoneIds.forEach(System.out::println);
            
          //獲取系統(tǒng)默認(rèn)時(shí)區(qū)
            ZoneId systemZoneId = ZoneId.systemDefault();
            System.out.println("當(dāng)期時(shí)區(qū): " + systemZoneId);
    
            // 獲取當(dāng)前時(shí)間日期
            ZonedDateTime date1 = ZonedDateTime.parse("2015-12-03T10:15:30+05:30[Asia/Shanghai]");
            System.out.println("date1: " + date1);
    
            //創(chuàng)建時(shí)區(qū)
            ZoneId id = ZoneId.of("Europe/Paris");
            System.out.println("ZoneId: " + id);
    
            //LocalDate越庇、LocalDateTime罩锐、Instant 轉(zhuǎn) ZonedDateTime
            ZonedDateTime zdt1 = LocalDate.of(2020, 9, 3).atStartOfDay(ZoneId.systemDefault());
            ZonedDateTime zdt2 = LocalDateTime.of(2020, 9, 3, 14, 10, 55, 888)
                                          .atZone(ZoneId.of("Asia/Shanghai"));
            ZonedDateTime zdt3 = Instant.now().atZone(ZoneId.of("Asia/Yerevan"));
    
            //Instant轉(zhuǎn)LocalDateTime
            LocalDateTime ldt1 = LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault());
        }
    
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市卤唉,隨后出現(xiàn)的幾起案子涩惑,更是在濱河造成了極大的恐慌,老刑警劉巖桑驱,帶你破解...
    沈念sama閱讀 223,126評(píng)論 6 520
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件竭恬,死亡現(xiàn)場(chǎng)離奇詭異跛蛋,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)痊硕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,421評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門赊级,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人岔绸,你說我怎么就攤上這事理逊。” “怎么了盒揉?”我有些...
    開封第一講書人閱讀 169,941評(píng)論 0 366
  • 文/不壞的土叔 我叫張陵晋被,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我刚盈,道長(zhǎng)羡洛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,294評(píng)論 1 300
  • 正文 為了忘掉前任藕漱,我火速辦了婚禮欲侮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘肋联。我一直安慰自己锈麸,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,295評(píng)論 6 398
  • 文/花漫 我一把揭開白布牺蹄。 她就那樣靜靜地躺著,像睡著了一般薄翅。 火紅的嫁衣襯著肌膚如雪沙兰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,874評(píng)論 1 314
  • 那天翘魄,我揣著相機(jī)與錄音鼎天,去河邊找鬼。 笑死暑竟,一個(gè)胖子當(dāng)著我的面吹牛斋射,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播但荤,決...
    沈念sama閱讀 41,285評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼罗岖,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了腹躁?” 一聲冷哼從身側(cè)響起桑包,我...
    開封第一講書人閱讀 40,249評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎纺非,沒想到半個(gè)月后哑了,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赘方,經(jīng)...
    沈念sama閱讀 46,760評(píng)論 1 321
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,840評(píng)論 3 343
  • 正文 我和宋清朗相戀三年弱左,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了窄陡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,973評(píng)論 1 354
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拆火,死狀恐怖跳夭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情榜掌,我是刑警寧澤优妙,帶...
    沈念sama閱讀 36,631評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站憎账,受9級(jí)特大地震影響套硼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜胞皱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,315評(píng)論 3 336
  • 文/蒙蒙 一邪意、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧反砌,春花似錦雾鬼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,797評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至酒贬,卻和暖如春又憨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背锭吨。 一陣腳步聲響...
    開封第一講書人閱讀 33,926評(píng)論 1 275
  • 我被黑心中介騙來泰國打工蠢莺, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人零如。 一個(gè)月前我還...
    沈念sama閱讀 49,431評(píng)論 3 379
  • 正文 我出身青樓躏将,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親考蕾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子祸憋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,982評(píng)論 2 361