都9012了,Java8中的日期時間API你尚未掌握?

一,Java8日期時間API產生的來龍去脈

1.1 爲何要從新定義一套日期時間APIjava

  • 操做不方便:java中最初的Date不能直接對指定字段進行加減操做也不支持國際化,後來新增了Calendar,可是Calendar又不支持格式化操做,須要轉換成Date再進行格式化,總之一直在填坑,使用起來一點都不夠優雅。
  • 線程不安全:Date,Caleandar,SimpleDateFormat都是可變的,線程不安全的,因此你須要編寫額外的代碼處理線程安全問題。

1.2 Java8從新定義mysql

  • 對時間日期相關操做進行細分:時間,日期,日期&時間,時間戳,時間段,日期段,格式化等
  • 全部類都是不可變的,線程安全
  • 兼容舊的日期時間

1.3Java8兼容就版本的Date,同時也規範了日期時間的轉換流程。redis

一,給人讀的( LocalDateTime & LocalDate & LocalTime)

java8中將時間和日期進行的區分,用LocalDateTime表示日期和時間,LocalDate用來表示日期而LocalTime表示時間。內部實現也很是好理解,LocalDateTime = LocalDate + LocalTime,而且他們的內部api也一致,因此筆者就結合工做中的經驗,介紹他們最多見的用法。sql

1.1 獲取當前時間api

LocalDateTime localDateTime = LocalDateTime.now();
// 打印結果: 2019-12-02T22:09:20.503複製代碼

1.2 獲取指定時間安全

// 獲取 2019年12月02號 23 : 59 : 59 
LocalDateTime localDateTime2 = LocalDateTime.of(2019, 12, 2, 23, 59, 59);
// 打印結果: 2019-12-02T13:20:20複製代碼

1.3 日期/時間加減操做併發

// localDateTime2的基礎上加1天零1s
LocalDateTime localDateTime3 = localDateTime2.plusDays(1).plusSeconds(1);
// 打印結果:2019-12-04T00:00複製代碼

1.4 獲取指定的字段(年月日時分秒,納秒,不支持毫秒)學習

System.out.println("如今是: " + localDateTime.getYear() + " 年中的第 " + localDateTime.getDayOfYear() +" 天");
// 打印結果:如今是: 2019 年中的第 336 天 
// 畫外音: 快過年了呀,感受這一年又沒啥收穫複製代碼

二,給計算機讀的(Instant)

小知識:地球上不一樣地區經度不一樣會劃分時區,以零度經線上爲準(格林尼治天文臺舊址,UTC時區)爲準,將地球上各個部分分爲了24個時區。向西走,每過一個時區,就要把表撥慢1個小時;同理每向東走一個時區,就要把表撥快1個小時。最後,中國處於東8區。測試

2.1 獲取UTC時間(格林尼治時間)spa

Instant instant = Instant.now();
// 打印結果: 2019-12-02T14:31:41.661Z複製代碼

2.2 獲取北京時間(東8區)

// OffsetTime表示有時差的時間,除了UTC時間,都是OffsetTime
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));
// 打印結果: 2019-12-02T22:31:41.661+08:00複製代碼

2.3 獲取毫秒數(1970-01-01T00:00:00Z開始計算)

long epochMilli = instant.toEpochMilli()
// 打印結果:1575297101661複製代碼

2.4 定義時間戳

Instant instant1 = Instant.ofEpochSecond(59);
// 打印結果: 1970-01-01T00:00:59Z
instant2 = instant1.plusSeconds(99)
// 打印結果:2019-12-02T14:43:00.402Z
複製代碼

三, 時間間隔(Duration)

3.1 計算日期間隔(參數位置影響結果哦)

Instant instant1 = Instant.now();
Instant instant2 = instant1.plusSeconds(99);

Duration duration1 = Duration.between(instant1, instant2);
Duration duration2 = Duration.between(instant2, instant1);
// 打印結果 duration1:PT1M39S
// 打印結果 duration2:PT-1M-39S

long duration1Seconds = duration1.getSeconds();
long duration2Seconds = duration1.getSeconds();
// 打印結果 duration1Seconds: 90
// 打印結果 duration2Seconds: -90複製代碼

3.2 操做時間間隔

Duration duration3 = duration1.plusDays(1);
// 打印結果:PT24H1M39S複製代碼

注意 : 僅支持時間操做(Instant, LocalTime,LocalDateTime),不支持日期(LocalDate)

四,日期間隔(Period)

LocalDate localDate1 = LocalDate.now();
LocalDate localDate2 = localDate1.plusDays(1);
Period period = Period.between(localDate1, localDate2);
long days =  period.getDays();
// 打印結果 peroid: P1D
// 打印結果 days: 1複製代碼

五,日期/時間校訂器(TemporalAdjuster)

5.1 獲取指定日期或時間

LocalDateTime localDateTime1 = LocalDateTime.now();
LocalDateTime localDateTime2 = localDateTime.withDayOfMonth(20);
// 打印結果 localDateTime1:2019-12-02T22:57:47.674
// 打印結果 localDateTime2:2019-12-20T22:57:47.674複製代碼

5.2 獲取下一個固定日期(下一個星期天)

LocalDateTime localDateTime3 = localDateTime.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
// 打印結果 localDateTime33:2019-12-08T23:00:43.101複製代碼

5.3 自定義矯正器

// 獲取下一個工做日
    LocalDateTime localDateTime4 = localDateTime.with((tempDateTime) -> {
            LocalDateTime localDateTime5 = (LocalDateTime) tempDateTime;
            DayOfWeek dayOfWeek = localDateTime5.getDayOfWeek();
            if (dayOfWeek.equals(DayOfWeek.FRIDAY)) {
                    return localDateTime5.plusDays(3);
            } else if (dayOfWeek.equals(DayOfWeek.SATURDAY)) {
                    return localDateTime5.plusDays(2);
            } else {
                    return localDateTime5.plusDays(1);
            }
    });
// 打印結果 localDateTime4:2019-12-03T23:00:43.101複製代碼

六,日期時間格式化

DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime localDateTime = LocalDateTime.now();
String dateStr =dateTimeFormatter.format(localDateTime);
// 打印結果: 2019-12-02 23:08:55
LocalDateTime localDateTime2 = LocalDateTime.parse(dateStr, dateTimeFormatter);
// 打印結果: 2019-12-02T23:08:55
LocalDate localDate = LocalDate.parse(dateStr, dateTimeFormatter);
// 打印結果: 2019-12-02複製代碼

七,基於Instant進行轉換

java8api對於時間戳,日期時間以及老版本的Date對象之間的轉換也進行了兼容和適配,全部的轉換操做均可以基於Instant對象進行。因爲LocalDate,LocalTime和LocalDateTime三個類的操做徹底同樣,因此下文仍使用LocalDateTime演示。

7.1 時間戳轉LocalDate,LocalDate,LocalDateTime

long timestamp = Instant.now().toEpochMilli();
LocalDateTime localDateTime = Instant.ofEpochMilli(timestamp).atOffset(ZoneOffset.ofHours(8)).toLocalDateTime();
// 打印結果:2019-12-02T23:20:25.791複製代碼

7.2 LocalDate,LocalDate,LocalDateTime轉時間戳

LocalDateTime localDateTime = LocalDateTime.now();
long timestamp = localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
// 打印結果:1575300368099 複製代碼

7.3 兼容就版本Date

LocalDateTime localDateTime3 = LocalDateTime.now();Date date = 
Date.from(localDateTime.atZone(ZoneOffset.ofHours(8)).toInstant());LocalDateTime localDateTime4  = 
localDateTime3.atZone(ZoneOffset.ofHours(8)).toLocalDateTime();
// 打印結果 date:Mon Dec 02 23:32:53 CST 2019
// 打印結果 lcoalDateTime4:2019-12-02T23:32:53.188複製代碼

八, Q&A

上一篇問題:在java中一般使用synchronized來實現方法同步,AQS中經過CAS保證了修改同步狀態的一致性問題,那麼對比synchronized,cas有什麼優點不一樣與優點呢?你還知道其餘無鎖併發的策略嗎?

8.1 Answer

Java中的無鎖併發策略能夠分爲三種:

  1. 基於樂觀鎖的CAS操做
  2. Copy On Write:寫時複製是指:在併發訪問的情景下,當須要修改JAVA中Containers的元素時,不直接修改該容器,而是先複製一份副本,在副本上進行修改。修改完成以後,將指向原來容器的引用指向新的容器(副本容器)
  3. ThreadLocal:線程本地存儲,就是爲每個線程建立一個變量,只有本線程能夠在該變量中查看和修改值。

8.2 Question

這是一道送分題:正如上文提到的,Java8以前的日期時間以及格式化類是線程不安全的,你知道怎麼編寫測試代碼嗎?

學習Java過程當中可能遇到問題和困惑,關注我vx公衆號 「cruder」 ,後臺留言,筆者幫你一塊兒解決!(須要學習資料的請關注後後臺留言,主要都是java相關,java基礎,併發,mysql,redis,es,mq等都都有!)若是你是一個初中級java開發工程師,究極自學怪,能夠關注筆者的公衆號cruder,你們一塊兒進步

相關文章
相關標籤/搜索