Java8 新特性之日期時(shí)間對(duì)象及一些其他特性
日期時(shí)間對(duì)象
關(guān)于日期時(shí)間的操作可以分為兩種:
- 轉(zhuǎn)換:與字符串的互相轉(zhuǎn)換,與時(shí)間戳的互相轉(zhuǎn)換
- 計(jì)算:計(jì)算兩個(gè)時(shí)間點(diǎn)之間的間隔、時(shí)間點(diǎn)與時(shí)間段的計(jì)算(計(jì)算下周N、下個(gè)月D日、去年M月D日等等)
Java8 提供了三個(gè)類(lèi):LocalDate、LocalTime、LocalDateTime,它們的形式如 2020-01-01、12:30:00、2020-01-01 12:30:00
創(chuàng)建對(duì)象
獲取類(lèi)對(duì)象的方法非常非常簡(jiǎn)單
LocalDate now = LocalDate.now(); LocalDate ld = LocalDate.of(2019, 1, 1); // 獲取年月日 now.getYear(); now.getMonthValue(); // 如果你調(diào)用了 now.getMonth() ,那么它將返回給你一個(gè)大寫(xiě)的英文月份單詞 now.getDayOfMonth(); // 顧名應(yīng)該思義 getDayOfWeek(); getDayOfYear(); // 設(shè)置年月日 LocalDate ld1 = ld.withYear(2021); // 2021-01-01 LocalDate ld2 = ld.withMonth(12); // 2019-12-01 LocalDate ld3 = ld.withDayOfMonth(12); // 2019-12-12 // 你可能會(huì)納悶,既然是設(shè)置,為什么不用單詞 set 呢,而用 with // 因?yàn)?,set 操作一般是改變調(diào)用對(duì)象本身,沒(méi)有返回值; // 而 with 是在調(diào)用對(duì)象基礎(chǔ)上另外創(chuàng)建一個(gè)新對(duì)象,設(shè)置好值后返回,沒(méi)有改變調(diào)用對(duì)象 // 如果你是那個(gè)打破砂鍋的孩子,你可能會(huì)問(wèn):為什么不能改變調(diào)用對(duì)象? // 因?yàn)?LocalDate 是 final 修飾的(final 人稱(chēng) Java 界的自宮之刀) // 從物理的角度來(lái)講,目前人類(lèi)無(wú)法改變時(shí)間(穿越) // 如果你有 ld.withMonth(13) 這種反人類(lèi)歷法的操作,當(dāng)然是會(huì)拋出異常的
LocalTime 和 LocalDateTime 都有類(lèi)似于 LocalDate 的方法,這里就不一一列舉了(因?yàn)槲腋杏X(jué)自己越來(lái)越像 api 文檔了)
轉(zhuǎn)換
日期時(shí)間對(duì)象 和 字符串 之間的互相轉(zhuǎn)換:
// LocalDateTime 對(duì)象 -> 字符串
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime now = LocalDateTime.now();
String dateTimeStr = now.format(dtf);
System.out.println(dateTimeStr);
// 字符串 -> LocalDateTime 對(duì)象
String str = "2022-01-30 12:15:20";
LocalDateTime dateTime = LocalDateTime.parse(str, dtf);
System.out.println(dateTime);
DateTimeFormatter 類(lèi)還提供一些現(xiàn)成的 formatter ,比如
DateTimeFormatter.BASIC_ISO_DATE ==> DateTimeFormatter.ofPattern("yyyyMMdd")
DateTimeFormatter.ISO_LOCAL_DATE ==> DateTimeFormatter.ofPattern("yyyy-MM-dd")
// 更多 formatter 可以 api 文檔中查詢(xún)
學(xué)習(xí)的本質(zhì),不在于記住哪些知識(shí),而在于它觸發(fā)了你的思考?!?邁克爾·桑德?tīng)?/p>
日期時(shí)間 和 時(shí)間戳 之間的互相轉(zhuǎn)換:
// LocalDateTime 對(duì)象 -> 時(shí)間戳 LocalDateTime now = LocalDateTime.now(); // 獲取系統(tǒng)默認(rèn)時(shí)區(qū) ZoneId systemDefaultZoneId = ZoneId.systemDefault(); Instant instant = now.atZone(systemDefaultZoneId).toInstant(); long timestamp = instant.toEpochMilli(); System.out.println(timestamp); // 時(shí)間戳 -> LocalDateTime 對(duì)象 long timestamp2 = 1578919583784L; Instant instant2 = Instant.ofEpochMilli(timestamp2); LocalDateTime dateTime2 = LocalDateTime.ofInstant(instant2, systemDefaultZoneId); System.out.println(dateTime2);
“我不明白為什么要把時(shí)間戳搞得這么麻煩!”
另外:java.util.Date 與 java.time.LocalDateTime 之間的轉(zhuǎn)換需要通過(guò) Instant 實(shí)現(xiàn),它倆都沒(méi)有提供直接的轉(zhuǎn)換方法
// 獲取系統(tǒng)默認(rèn)時(shí)區(qū) ZoneId systemDefaultZoneId = ZoneId.systemDefault(); // Date 轉(zhuǎn)為 LocalDateTime Date date3 = new Date(); Instant instant3 = date3.toInstant(); LocalDateTime localDateTime3 = LocalDateTime.ofInstant(instant3, systemDefaultZoneId); // LocalDateTime 轉(zhuǎn)為 Date Instant instant4 = now.atZone(systemDefaultZoneId).toInstant(); Date date4 = Date.from(instant4);
還有:LocalDateTime 可以由 LocalDate 和 LocalTime 組成,也可以拆分成它倆
LocalDate nowLocalDate = LocalDate.now(); LocalTime nowLocalTime = LocalTime.now(); LocalDateTime nowLocalDateTime = LocalDateTime.of(nowLocalDate, nowLocalTime); nowLocalDateTime.toLocalDate(); nowLocalDateTime.toLocalTime();
計(jì)算
計(jì)算時(shí)間點(diǎn)與時(shí)間點(diǎn)之間的間隔:
// 計(jì)算日期時(shí)間之間的間隔 LocalTime startTime = LocalTime.now(); LocalTime endTime = startTime.plusHours(1).plusMinutes(50); Duration duration = Duration.between(startTime, endTime); // 間隔秒數(shù) duration.getSeconds(); // 間隔天數(shù) duration.toDays(); // 間隔小時(shí)數(shù) duration.toHours(); // 間隔分鐘數(shù) duration.toMinutes();
Duration.between(start, end) 的參數(shù)可以是 LocalDateTime 、LocalTime
它只會(huì)返回一個(gè)整數(shù)(舍掉小數(shù)后的整數(shù),等同于 floor()),不會(huì)返回 1小時(shí)50分鐘 這樣的形式
如果你想要 y年M個(gè)月d天 H小時(shí)m分鐘s秒 這種形式,或許你自己動(dòng)手組裝一下了
// 計(jì)算日期之間的間隔 LocalDate startDate = LocalDate.now(); LocalDate endDate = LocalDate.of(2031, 1, 1); Period pe = Period.between(startDate, endDate); pe.getYears(); pe.getMonths(); pe.getDays();
時(shí)間點(diǎn)與時(shí)間段的計(jì)算:
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);
}
看吧,加減年數(shù)、月數(shù)、天數(shù)、小時(shí)數(shù)、分鐘數(shù)、秒數(shù)、毫秒數(shù)都有
想怎么用就怎么用,舉個(gè)小例子:
LocalDateTime now = LocalDateTime.now(); // 30年后的今天(我還要上班,還沒(méi)退休) LocalDateTime after30Years = now.plusYears(30L); System.out.println(after30Years); // 347個(gè)月后(我就能還清貸款了 ╥﹏╥) LocalDateTime after348Months = now.plusMonths(347L); System.out.println(after348Months); // 11天后(就是除夕了) LocalDateTime after10Days = now.plusDays(10L); System.out.println(after10Days); // 8小時(shí)前(我在上班) LocalDateTime before8Hours = now.minusHours(8L); System.out.println(before8Hours); // 3分鐘前(我開(kāi)始聽(tīng) Let it go 這首歌) LocalDateTime before3Before = now.minusMinutes(3L); System.out.println(before3Before); // 10秒前(寫(xiě)下下面這條代碼) LocalDateTime before10Second = now.minusSeconds(10L); System.out.println(before10Second);
其實(shí)不用區(qū)分什么加減的,也可以用 plusXxx 做減法,只要傳入負(fù)數(shù)參數(shù)就行了
另外這里還有一類(lèi)需求,比如:
明年的感恩節(jié)是哪天?(每年11月的第四個(gè)星期四為感恩節(jié))
下周五是哪天?
這就要用到時(shí)間校正器 TemporalAdjuster
這里容我先介紹一下 TemporalAdjuster,它是一個(gè)函數(shù)式接口,只有一個(gè)方法 adjustInto
@FunctionalInterface
public interface TemporalAdjuster {
Temporal adjustInto(Temporal temporal);
}
Temporal 是一個(gè)接口,LocalDateTime 、LocalDate 、 LocalTime 都是它的實(shí)現(xiàn)類(lèi)
在 LocalDateTime 、LocalDate 、 LocalTime 中都有 with(TemporalAdjuster adjuster) 這個(gè)方法用來(lái)實(shí)現(xiàn)上面提到的另類(lèi)需求。
// 下周五
LocalDateTime now = LocalDateTime.now();
LocalDateTime nextFriday = now.with(dt -> {
// dt 是 `Temporal` 對(duì)象,但實(shí)質(zhì)上是調(diào)用對(duì)象的類(lèi)型
LocalDateTime dateTime = (LocalDateTime) dt;
// 非??上?,沒(méi)有 withDayOfWeek() 這個(gè)方法,要不然就會(huì)非常方便了
int dayOfWeekValue = dateTime.getDayOfWeek().getValue();
int fridayValue = DayOfWeek.FRIDAY.getValue();
return dateTime.plusWeeks(1L)
.plusDays(fridayValue - dayOfWeekValue);
});
System.out.println(nextFriday);
// 明年的感恩節(jié)(明年11月第四個(gè)星期四)
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);
其實(shí) TemporalAdjusters 提供了許多 TemporalAdjuster 對(duì)象,就像上一節(jié) Stream 中 Collectors 之于 Collector 一樣 。
使用 TemporalAdjusters 能夠十分方便的實(shí)現(xiàn)上面的需求
// 下個(gè)周五 LocalDateTime nextFriday = LocalDateTime.now().with(TemporalAdjusters.next(DayOfWeek.FRIDAY)); System.out.println(nextFriday); // 明年的感恩節(jié)(明年11月第四個(gè)星期四) 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) 方法返回的是 接下來(lái)第一個(gè)周五,并不是我們一般理解的 下周五,比如說(shuō):今天 2020-01-13(周一),那么返回的就是 2020-01-17 四天后的周五。
另外 TemporalAdjusters 并不止提供了上面這2個(gè)方法,還有很多其他方法, API 文檔 中給出了足夠多的例子,一看就明白了。
建議有興趣的同學(xué),去閱讀一些源碼,參考 Java8 代碼邏輯,然后用其他編程語(yǔ)言實(shí)現(xiàn)相同的日期時(shí)間操作,因?yàn)樵谄渌幊讨校ū热?javaScript)也會(huì)經(jīng)常用到日期時(shí)間的操作
其他特性
羅列出來(lái)表示我知道他們,但不表示我理解他們,所以 Let It Go!
接口中的默認(rèn)方法
接口中的靜態(tài)方法
Optional 類(lèi)
Optional<String> op = Optional.of(str);
它是用來(lái)標(biāo)識(shí)這個(gè)變量有可能為空。
如果一個(gè)變量有可能為空,Java8 之前我們每次使用這個(gè)變量時(shí),都必須判斷它是否為空。現(xiàn)在也是??!
Optional 并不能避免空指針異常,僅僅是表示標(biāo)識(shí)變量可能為空。打個(gè)比方:
前面一條路上埋了地雷,但從表面上完全看不出來(lái),除非我們走一步都扔石頭試一下,否則說(shuō)不準(zhǔn)哪一步就炸了。而 Optional就是用來(lái)標(biāo)識(shí)地雷位置的,我們知道了哪個(gè)位置有雷,就會(huì)繞著走,從而能夠安全通過(guò)
另外 Optional 還提供了一個(gè)設(shè)置默認(rèn)值的功能,挺好玩的。
Integer pageSize = null;
// 以前我們?cè)O(shè)置默認(rèn)值
//pageSize = pageSize == null ? 10 : pageSize;
//System.out.println(pageSize);
// 使用 Optional 設(shè)置默認(rèn)值
pageSize = Optional.ofNullable(pageSize)
.orElse(20);
System.out.println(pageSize);
// 自定義默認(rèn)值
Integer defaultPageSize = Optional.ofNullable(pageSize)
.orElseGet(() -> {
return new Integer(50);
});
結(jié)語(yǔ)
Java8 新特性系列隨便到此就結(jié)束了。
最最關(guān)鍵的,還是多看 Java 官方 API 文檔!!
我在想可能我們被矯枉過(guò)正了。各種各樣技術(shù)群最多的回答都是:去問(wèn)百度??! 百度全知道嗎?!
說(shuō)實(shí)在的,在今天這個(gè)時(shí)代,是個(gè)人都能在網(wǎng)絡(luò)上發(fā)表文章言論,然后大家再互相轉(zhuǎn)載,假的都能成為真的!
沒(méi)有經(jīng)過(guò)驗(yàn)證就轉(zhuǎn)載的;在當(dāng)時(shí)有效,現(xiàn)在過(guò)時(shí)了的;隨便在文章中一個(gè)轉(zhuǎn)載鏈接的 ... 比比皆是
總結(jié)
以上所述是小編給大家介紹的Java8 新特性之日期時(shí)間對(duì)象及一些其他特性,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
如果你覺(jué)得本文對(duì)你有幫助,歡迎轉(zhuǎn)載,煩請(qǐng)注明出處,謝謝!
相關(guān)文章
淺談Java設(shè)計(jì)模式之原型模式知識(shí)總結(jié)
Java原型模式主要用于創(chuàng)建重復(fù)的對(duì)象,同時(shí)又能保證性能,這篇文章就帶大家仔細(xì)了解一下原型模式的知識(shí),對(duì)正在學(xué)習(xí)java的小伙伴們很有幫助,需要的朋友可以參考下2021-05-05
maven項(xiàng)目后出現(xiàn)‘parent.relativePath’ of POM錯(cuò)誤時(shí)的解決方法
在Springboot項(xiàng)目啟動(dòng)時(shí),項(xiàng)目報(bào)錯(cuò)‘parent.relativePath’ of POM問(wèn)題,項(xiàng)目無(wú)法正常啟動(dòng),本文就來(lái)介紹一下解決方法,感興趣的可以了解一下2023-10-10
Java編程實(shí)現(xiàn)漢字按字母順序排序的方法示例
這篇文章主要介紹了Java編程實(shí)現(xiàn)漢字按字母順序排序的方法,結(jié)合具體實(shí)例形式分析了java編碼轉(zhuǎn)換及字母排序相關(guān)操作技巧,需要的朋友可以參考下2017-07-07
Spring項(xiàng)目中swagger用法與swagger-ui使用
這篇文章主要介紹了Spring項(xiàng)目中swagger用法與swagger-ui使用,通過(guò)圖文并茂的形式給大家介紹了編寫(xiě)springboot項(xiàng)目的方法及導(dǎo)入spring-fox依賴(lài)的代碼詳解,需要的朋友可以參考下2021-05-05
解決springboot錯(cuò)誤:找不到或無(wú)法加載主類(lèi)(配置編碼或者M(jìn)aven)
這篇文章主要介紹了解決springboot錯(cuò)誤:找不到或無(wú)法加載主類(lèi)(配置編碼或者M(jìn)aven)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06
JVM調(diào)整java虛擬機(jī)可使用的最大內(nèi)存的方法
本文主要介紹了調(diào)整JVM的內(nèi)存參數(shù)來(lái)優(yōu)化Java應(yīng)用程序的性能,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2025-01-01
SpringMVC @RequestBody 為null問(wèn)題的排查及解決
這篇文章主要介紹了SpringMVC @RequestBody 為null問(wèn)題的排查及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10
java訪(fǎng)問(wèn)者模式的靜態(tài)動(dòng)態(tài)及偽動(dòng)態(tài)分派徹底理解
這篇文章主要為大家介紹了java訪(fǎng)問(wèn)者模式的靜態(tài)動(dòng)態(tài)及偽動(dòng)態(tài)分派徹底理解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06

