Java8 新特性(三) - 日期時間對象以及一些其餘特性

日期時間對象

關於日期時間的操做能夠分爲兩種:html

  • 轉換:與字符串的互相轉換,與時間戳的互相轉換
  • 計算:計算兩個時間點之間的間隔、時間點與時間段的計算(計算下週N、下個月D日、去年M月D日等等)

Java8 提供了三個類:LocalDateLocalTimeLocalDateTime,它們的形式如 2020-01-0112:30:002020-01-01 12:30:00java

建立對象

獲取類對象的方法很是很是簡單編程

LocalDate now = LocalDate.now();
LocalDate ld = LocalDate.of(2019, 1, 1);
// 獲取年月日
now.getYear();
now.getMonthValue();	// 若是你調用了 now.getMonth() ,那麼它將返回給你一個大寫的英文月份單詞
now.getDayOfMonth();
// 顧名應該思義
getDayOfWeek();
getDayOfYear();	

// 設置年月日
LocalDate ld1 = ld.withYear(2021);		// 2021-01-01
LocalDate ld2 = ld.withMonth(12);		// 2019-12-01
LocalDate ld3 = ld.withDayOfMonth(12);	// 2019-12-12
// 你可能會納悶,既然是設置,爲何不用單詞 set 呢,而用 with
// 由於,set 操做通常是改變調用對象自己,沒有返回值;
// 而 with 是在調用對象基礎上另外建立一個新對象,設置好值後返回,沒有改變調用對象

// 若是你是那個打破砂鍋的孩子,你可能會問:爲何不能改變調用對象?
// 由於 LocalDate 是 final 修飾的(final 人稱 Java 界的自宮之刀)
// 從物理的角度來說,目前人類沒法改變時間(穿越)

// 若是你有 ld.withMonth(13) 這種反人類曆法的操做,固然是會拋出異常的

LocalTimeLocalDateTime 都有相似於 LocalDate 的方法,這裏就不一一列舉了(由於我感受本身愈來愈像 api 文檔了)api

Java8 API 官方文檔直通車安全

轉換

日期時間對象 和 字符串 之間的互相轉換:網絡

// LocalDateTime 對象 -> 字符串
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime now = LocalDateTime.now();
String dateTimeStr = now.format(dtf);
System.out.println(dateTimeStr);

// 字符串 -> LocalDateTime 對象
String str = "2022-01-30 12:15:20";
LocalDateTime dateTime = LocalDateTime.parse(str, dtf);
System.out.println(dateTime);

DateTimeFormatter 類還提供一些現成的 formatter ,好比oracle

DateTimeFormatter.BASIC_ISO_DATE ==> DateTimeFormatter.ofPattern("yyyyMMdd")
DateTimeFormatter.ISO_LOCAL_DATE ==> DateTimeFormatter.ofPattern("yyyy-MM-dd")
// 更多 formatter 能夠 api 文檔中查詢

學習的本質,不在於記住哪些知識,而在於它觸發了你的思考。—— 邁克爾·桑德爾編程語言

日期時間 和 時間戳 之間的互相轉換:函數

// LocalDateTime 對象 -> 時間戳
LocalDateTime now = LocalDateTime.now();
// 獲取系統默認時區
ZoneId systemDefaultZoneId = ZoneId.systemDefault();
Instant instant = now.atZone(systemDefaultZoneId).toInstant();
long timestamp = instant.toEpochMilli();
System.out.println(timestamp);


// 時間戳 -> LocalDateTime 對象
long timestamp2 = 1578919583784L;
Instant instant2 = Instant.ofEpochMilli(timestamp2);
LocalDateTime dateTime2 = LocalDateTime.ofInstant(instant2, systemDefaultZoneId);
System.out.println(dateTime2);

「我不明白爲何要把時間戳搞得這麼麻煩!」學習

另外:java.util.Datejava.time.LocalDateTime 之間的轉換須要經過 Instant 實現,它倆都沒有提供直接的轉換方法

// 獲取系統默認時區
ZoneId systemDefaultZoneId = ZoneId.systemDefault();
// Date 轉爲 LocalDateTime
Date date3 = new Date();
Instant instant3 = date3.toInstant();
LocalDateTime localDateTime3 = LocalDateTime.ofInstant(instant3, systemDefaultZoneId);

// LocalDateTime 轉爲 Date
Instant instant4 = now.atZone(systemDefaultZoneId).toInstant();
Date date4 = Date.from(instant4);

還有:LocalDateTime 能夠由 LocalDateLocalTime 組成,也能夠拆分紅它倆

LocalDate nowLocalDate = LocalDate.now();
LocalTime nowLocalTime = LocalTime.now();
LocalDateTime nowLocalDateTime = LocalDateTime.of(nowLocalDate, nowLocalTime);

nowLocalDateTime.toLocalDate();
nowLocalDateTime.toLocalTime();

計算

計算時間點與時間點之間的間隔:

// 計算日期時間之間的間隔
LocalTime startTime = LocalTime.now();
LocalTime endTime = startTime.plusHours(1).plusMinutes(50);
Duration duration = Duration.between(startTime, endTime);

// 間隔秒數
duration.getSeconds();
// 間隔天數
duration.toDays();
// 間隔小時數
duration.toHours();
// 間隔分鐘數
duration.toMinutes();

Duration.between(start, end) 的參數能夠是 LocalDateTimeLocalTime

它只會返回一個整數(舍掉小數後的整數,等同於 floor()),不會返回 1小時50分鐘 這樣的形式 若是你想要 y年M個月d天 H小時m分鐘s秒 這種形式,或許你本身動手組裝一下了

// 計算日期之間的間隔
LocalDate startDate = LocalDate.now();
LocalDate endDate = LocalDate.of(2031, 1, 1);
Period pe = Period.between(startDate, endDate);
pe.getYears();
pe.getMonths();
pe.getDays();

時間點與時間段的計算:

public LocalDateTime plusYears(long years) {
        LocalDate newDate = date.plusYears(years);
        return with(newDate, time);
    }

    public LocalDateTime plusMonths(long months) {
        LocalDate newDate = date.plusMonths(months);
        return with(newDate, time);
    }

    public LocalDateTime plusWeeks(long weeks) {
        LocalDate newDate = date.plusWeeks(weeks);
        return with(newDate, time);
    }

    public LocalDateTime plusDays(long days) {
        LocalDate newDate = date.plusDays(days);
        return with(newDate, time);
    }

    public LocalDateTime plusHours(long hours) {
        return plusWithOverflow(date, hours, 0, 0, 0, 1);
    }

    public LocalDateTime plusMinutes(long minutes) {
        return plusWithOverflow(date, 0, minutes, 0, 0, 1);
    }

    public LocalDateTime plusSeconds(long seconds) {
        return plusWithOverflow(date, 0, 0, seconds, 0, 1);
    }

    public LocalDateTime plusNanos(long nanos) {
        return plusWithOverflow(date, 0, 0, 0, nanos, 1);
    }

    public LocalDateTime minusYears(long years) {
        return (years == Long.MIN_VALUE ? plusYears(Long.MAX_VALUE).plusYears(1) : plusYears(-years));
    }

    public LocalDateTime minusMonths(long months) {
        return (months == Long.MIN_VALUE ? plusMonths(Long.MAX_VALUE).plusMonths(1) : plusMonths(-months));
    }

    public LocalDateTime minusWeeks(long weeks) {
        return (weeks == Long.MIN_VALUE ? plusWeeks(Long.MAX_VALUE).plusWeeks(1) : plusWeeks(-weeks));
    }

    public LocalDateTime minusDays(long days) {
        return (days == Long.MIN_VALUE ? plusDays(Long.MAX_VALUE).plusDays(1) : plusDays(-days));
    }

    public LocalDateTime minusHours(long hours) {
        return plusWithOverflow(date, hours, 0, 0, 0, -1);
   }

    public LocalDateTime minusMinutes(long minutes) {
        return plusWithOverflow(date, 0, minutes, 0, 0, -1);
    }

    public LocalDateTime minusSeconds(long seconds) {
        return plusWithOverflow(date, 0, 0, seconds, 0, -1);
    }

    public LocalDateTime minusNanos(long nanos) {
        return plusWithOverflow(date, 0, 0, 0, nanos, -1);
    }

看吧,加減年數、月數、天數、小時數、分鐘數、秒數、毫秒數都有

想怎麼用就怎麼用,舉個小例子:

LocalDateTime now = LocalDateTime.now();

// 30年後的今天(我還要上班,還沒退休)
LocalDateTime after30Years = now.plusYears(30L);
System.out.println(after30Years);

// 347個月後(我就能還清貸款了 ╥﹏╥)
LocalDateTime after348Months = now.plusMonths(347L);
System.out.println(after348Months);

// 11天后(就是除夕了)
LocalDateTime after10Days = now.plusDays(10L);
System.out.println(after10Days);

// 8小時前(我在上班)
LocalDateTime before8Hours = now.minusHours(8L);
System.out.println(before8Hours);

// 3分鐘前(我開始聽 Let it go 這首歌)
LocalDateTime before3Before = now.minusMinutes(3L);
System.out.println(before3Before);

// 10秒前(寫下下面這條代碼)
LocalDateTime before10Second = now.minusSeconds(10L);
System.out.println(before10Second);

其實不用區分什麼加減的,也能夠用 plusXxx 作減法,只要傳入負數參數就好了

另外這裏還有一類需求,好比:

明年的感恩節是哪天?(每一年11月的第四個星期四爲感恩節) 下週五是哪天?

這就要用到時間校訂器 TemporalAdjuster
這裏容我先介紹一下 TemporalAdjuster,它是一個函數式接口,只有一個方法 adjustInto

@FunctionalInterface
public interface TemporalAdjuster {
    Temporal adjustInto(Temporal temporal);
}

Temporal 是一個接口,LocalDateTimeLocalDateLocalTime 都是它的實現類

LocalDateTimeLocalDateLocalTime 中都有 with(TemporalAdjuster adjuster) 這個方法用來實現上面提到的另類需求。

// 下週五
LocalDateTime now = LocalDateTime.now();
LocalDateTime nextFriday = now.with(dt -> {
    // dt 是 `Temporal` 對象,但實質上是調用對象的類型
    LocalDateTime dateTime = (LocalDateTime) dt;
    // 很是惋惜,沒有 withDayOfWeek() 這個方法,要否則就會很是方便了
    int dayOfWeekValue = dateTime.getDayOfWeek().getValue();
    int fridayValue = DayOfWeek.FRIDAY.getValue();
    return dateTime.plusWeeks(1L)
        		.plusDays(fridayValue - dayOfWeekValue);
});
System.out.println(nextFriday);

// 明年的感恩節(明年11月第四個星期四)
LocalDate thanksGivingDay = LocalDate.now().with( t -> {
    LocalDate d = (LocalDate) t;
    // 明年11月1日
    LocalDate newDate = d.plusYears(1L).withMonth(11).withDayOfMonth(1);

    int dayOfWeekValue = newDate.getDayOfWeek().getValue();
    int thursdayValue = DayOfWeek.THURSDAY.getValue();
    long plusWeeks = dayOfWeekValue > thursdayValue ? 4L : 3L;
    return newDate.plusWeeks(plusWeeks)
        			.plusDays(thursdayValue - dayOfWeekValue);
});
System.out.println(thanksGivingDay);

其實 TemporalAdjusters 提供了許多 TemporalAdjuster 對象,就像上一節 Stream 中 Collectors 之於 Collector 同樣 。

使用 TemporalAdjusters 可以十分方便的實現上面的需求

// 下個週五
LocalDateTime nextFriday = LocalDateTime.now().with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
System.out.println(nextFriday);

// 明年的感恩節(明年11月第四個星期四)
LocalDate date = LocalDate.now().plusYears(1L).withMonth(11);
LocalDate thanksGivingDay = date.with(TemporalAdjusters.dayOfWeekInMonth(4, DayOfWeek.THURSDAY));
System.out.println(thanksGivingDay);

注意: TemporalAdjusters.next(DayOfWeek day) 方法返回的是 接下來第一個週五,並非咱們通常理解的 下週五,好比說:今天 2020-01-13(週一),那麼返回的就是 2020-01-17 四天後的週五。

另外 TemporalAdjusters 並不止提供了上面這2個方法,還有不少其餘方法, API 文檔 中給出了足夠多的例子,一看就明白了。

建議有興趣的同窗,去閱讀一些源碼,參考 Java8 代碼邏輯,而後用其餘編程語言實現相同的日期時間操做,由於在其餘編程中(好比 javaScript)也會常常用到日期時間的操做

其餘特性

羅列出來表示我知道他們,但不表示我理解他們,因此 Let It Go!

接口中的默認方法

接口中的靜態方法

Optional 類

Optional<String> op = Optional.of(str);

它是用來標識這個變量有可能爲空。

若是一個變量有可能爲空,Java8 以前咱們每次使用這個變量時,都必須判斷它是否爲空。如今也是!!

Optional 並不能避免空指針異常,僅僅是表示標識變量可能爲空。打個比方:

前面一條路上埋了地雷,但從表面上徹底看不出來,除非咱們走一步都扔石頭試一下,不然說不許哪一步就炸了。而 Optional 就是用來標識地雷位置的,咱們知道了哪一個位置有雷,就會繞着走,從而可以安全經過

另外 Optional 還提供了一個設置默認值的功能,挺好玩的。

Integer pageSize = null;
// 之前咱們設置默認值
//pageSize = pageSize == null ? 10 : pageSize;
//System.out.println(pageSize);

// 使用 Optional 設置默認值
pageSize = Optional.ofNullable(pageSize)
    .orElse(20);
System.out.println(pageSize);

// 自定義默認值
Integer defaultPageSize = Optional.ofNullable(pageSize)
        .orElseGet(() -> {
            return new Integer(50);
        });

結語

Java8 新特性系列隨便到此就結束了。

最最關鍵的,仍是多看 Java 官方 API 文檔!!

我在想可能咱們被矯枉過正了。各類各樣技術羣最多的回答都是:去問百度!! 百度全知道嗎?!

說實在的,在今天這個時代,是我的都能在網絡上發表文章言論,而後你們再互相轉載,假的都能成爲真的! 沒有通過驗證就轉載的;在當時有效,如今過期了的;隨便在文章中一個轉載連接的 ... 比比皆是

這裏有一個開眼的短視頻,介紹 虛假新聞是如何在傳播

最惱人的是,百度搜索一個關鍵詞 XXX,點進去一篇博客文章,結果文章內容根本就沒有這個關鍵詞,那麼關鍵詞到底在哪呢? 關鍵詞在網站的推薦文章列表標題裏,因此百度爬取到了,顯示在搜索結果裏了。真是日了狗了!!

百度搜到的博客能別看就別看,看也要看那些常常更新,比較靠譜的。

靠譜的學習途徑:

  1. 官方最新 API 文檔
  2. 書籍/視頻(要注意權威性和時效性)
  3. 常常更新靠譜的博客

不要過於信任搜索引擎!! 輕易不要使用搜索引擎!!

原文出處:https://www.cnblogs.com/lhat/p/12216669.html

相關文章
相關標籤/搜索