Java 8:新的時(shí)間和日期API
在Java 8之前,所有關(guān)于時(shí)間和日期的API都存在各種使用方面的缺陷型型,因此建議使用新的時(shí)間和日期API段审,分別從舊的時(shí)間和日期的API的缺點(diǎn)以及解決方法、Java 8 新的時(shí)間和日期API進(jìn)行講解闹蒜。
舊的時(shí)間和日期的API的缺陷
Java 的 java.util.Date 和 java.util.Calendar 類易用性差寺枉,不支持時(shí)區(qū)抑淫,而且都不是線程安全的。
Date如果不格式化姥闪,打印出的日期可讀性差始苇。
Thu Sep 12 13:47:34 CST 2019
可以使用 SimpleDateFormat 對時(shí)間進(jìn)行格式化,但 SimpleDateFormat 是線程不安全的筐喳,SimpleDateFormat 的 format 方法源碼如下:
private StringBuffer format(Date date, StringBuffer toAppendTo,
FieldDelegate delegate) {
// Convert input date to time field list
calendar.setTime(date);
boolean useDateFormatSymbols = useDateFormatSymbols();
for (int i = 0; i < compiledPattern.length; ) {
int tag = compiledPattern[i] >>> 8;
int count = compiledPattern[i++] & 0xff;
if (count == 255) {
count = compiledPattern[i++] << 16;
count |= compiledPattern[i++];
}
switch (tag) {
case TAG_QUOTE_ASCII_CHAR:
toAppendTo.append((char)count);
break;
case TAG_QUOTE_CHARS:
toAppendTo.append(compiledPattern, i, count);
i += count;
break;
default:
subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);
break;
}
}
return toAppendTo;
}
其中 calendar 是共享變量催式,并且這個(gè)共享變量沒有做線程安全控制。當(dāng)多個(gè)線程同時(shí)使用相同的 SimpleDateFormat 對象【如用static修飾的 SimpleDateFormat 】調(diào)用format方法時(shí)疏唾,多個(gè)線程會(huì)同時(shí)調(diào)用 calendar.setTime 方法蓄氧,可能一個(gè)線程剛設(shè)置好 time 值另外的一個(gè)線程馬上把設(shè)置的 time 值給修改了導(dǎo)致返回的格式化時(shí)間可能是錯(cuò)誤的。
在多并發(fā)情況下使用 SimpleDateFormat 需注意槐脏。
SimpleDateFormat 除了 format 是線程不安全以外喉童,parse 方法也是線程不安全的。parse 方法實(shí)際調(diào)用 alb.establish(calendar).getTime() 方法來解析顿天,alb.establish(calendar) 方法里主要完成了
- 重置日期對象cal的屬性值
- 使用calb中中屬性設(shè)置cal
- 返回設(shè)置好的cal對象
但是這三步不是原子操作堂氯,導(dǎo)致解析出來的時(shí)間可以是錯(cuò)誤的。
Date對時(shí)間處理比較麻煩牌废,比如想獲取某年咽白、某月、某星期鸟缕,以及 n 天以后的時(shí)間晶框,如果用Date來處理的話真是太難了,并且 Date 類的 getYear懂从、getMonth 這些方法都被棄用了授段。
多線程并發(fā)如何保證線程安全
避免線程之間共享一個(gè) SimpleDateFormat 對象,每個(gè)線程使用時(shí)都創(chuàng)建一次 SimpleDateFormat 對象 => 創(chuàng)建和銷毀對象的開銷大
對使用 format 和 parse 方法的地方進(jìn)行加鎖 => 線程阻塞性能差
使用 ThreadLocal 保證每個(gè)線程最多只創(chuàng)建一次 SimpleDateFormat 對象 => 較好的方法
Java 8 新的時(shí)間和日期API
Java 8的日期和時(shí)間類包含 LocalDate番甩、LocalTime侵贵、Instant、Duration 以及 Period缘薛,這些類都包含在 java.time 包中窍育,Java 8 新的時(shí)間API的使用方式,包括創(chuàng)建宴胧、格式化漱抓、解析、計(jì)算恕齐、修改辽旋,下面我們看下如何去使用。
LocalDate 只會(huì)獲取年月日
// 創(chuàng)建 LocalDate
// 獲取當(dāng)前年月日
LocalDate localDate = LocalDate.now();
// 構(gòu)造指定的年月日
LocalDate localDate1 = LocalDate.of(2019, 9, 12);
// 獲取年、月补胚、日码耐、星期幾
int year = localDate.getYear();
int year1 = localDate.get(ChronoField.YEAR);
Month month = localDate.getMonth();
int month1 = localDate.get(ChronoField.MONTH_OF_YEAR);
int day = localDate.getDayOfMonth();
int day1 = localDate.get(ChronoField.DAY_OF_MONTH);
DayOfWeek dayOfWeek = localDate.getDayOfWeek();
int dayOfWeek1 = localDate.get(ChronoField.DAY_OF_WEEK);
LocalTime 只會(huì)獲取時(shí)分秒
// 創(chuàng)建 LocalTime
LocalTime localTime = LocalTime.of(14, 14, 14);
LocalTime localTime1 = LocalTime.now();
// 獲取小時(shí)
int hour = localTime.getHour();
int hour1 = localTime.get(ChronoField.HOUR_OF_DAY);
// 獲取分
int minute = localTime.getMinute();
int minute1 = localTime.get(ChronoField.MINUTE_OF_HOUR);
// 獲取秒
int second = localTime.getMinute();
int second1 = localTime.get(ChronoField.SECOND_OF_MINUTE);
LocalDateTime 獲取年月日時(shí)分秒,相當(dāng)于 LocalDate + LocalTime
// 創(chuàng)建 LocalDateTime
LocalDateTime localDateTime = LocalDateTime.now();
LocalDateTime localDateTime1 = LocalDateTime.of(2019, Month.SEPTEMBER, 10, 14, 46, 56);
LocalDateTime localDateTime2 = LocalDateTime.of(localDate, localTime);
LocalDateTime localDateTime3 = localDate.atTime(localTime);
LocalDateTime localDateTime4 = localTime.atDate(localDate);
// 獲取LocalDate
LocalDate localDate2 = localDateTime.toLocalDate();
// 獲取LocalTime
LocalTime localTime2 = localDateTime.toLocalTime();
Instant 獲取秒數(shù)溶其,用于表示一個(gè)時(shí)間戳(精確到納秒)
如果只是為了獲取秒數(shù)或者毫秒數(shù)骚腥,可以使用 System.currentTimeMillis()。
// 創(chuàng)建Instant對象
Instant instant = Instant.now();
// 獲取秒數(shù)
long currentSecond = instant.getEpochSecond();
// 獲取毫秒數(shù)
long currentMilli = instant.toEpochMilli();
Duration 表示一個(gè)時(shí)間段
// Duration.between()方法創(chuàng)建 Duration 對象
LocalDateTime from = LocalDateTime.of(2017, Month.JANUARY, 1, 00, 0, 0); // 2017-01-01 00:00:00
LocalDateTime to = LocalDateTime.of(2019, Month.SEPTEMBER, 12, 14, 28, 0); // 2019-09-15 14:28:00
Duration duration = Duration.between(from, to); // 表示從 from 到 to 這段時(shí)間
long days = duration.toDays(); // 這段時(shí)間的總天數(shù)
long hours = duration.toHours(); // 這段時(shí)間的小時(shí)數(shù)
long minutes = duration.toMinutes(); // 這段時(shí)間的分鐘數(shù)
long seconds = duration.getSeconds(); // 這段時(shí)間的秒數(shù)
long milliSeconds = duration.toMillis(); // 這段時(shí)間的毫秒數(shù)
long nanoSeconds = duration.toNanos(); // 這段時(shí)間的納秒數(shù)
修改 LocalDate瓶逃、LocalTime束铭、LocalDateTime、Instant厢绝。
LocalDate契沫、LocalTime、LocalDateTime昔汉、Instant 為不可變對象懈万,修改這些對象對象會(huì)返回一個(gè)副本。
增加靶病、減少年數(shù)会通、月數(shù)、天數(shù)等娄周,以LocalDateTime為例:
LocalDateTime localDateTime = LocalDateTime.of(2019, Month.SEPTEMBER, 12, 14, 32, 0);
// 增加一年
localDateTime = localDateTime.plusYears(1);
localDateTime = localDateTime.plus(1, ChronoUnit.YEARS);
// 減少一個(gè)月
localDateTime = localDateTime.minusMonths(1);
localDateTime = localDateTime.minus(1, ChronoUnit.MONTHS);
// 通過with修改某些值
// 修改年為2020
localDateTime = localDateTime.withYear(2020);
localDateTime = localDateTime.with(ChronoField.YEAR, 2020);
// 時(shí)間計(jì)算
// 獲取該年的第一天
LocalDate localDate = LocalDate.now();
LocalDate localDate1 = localDate.with(firstDayOfYear());
TemporalAdjusters 包含許多靜態(tài)方法,可以直接調(diào)用裳涛,以下列舉一些:
方法名 | 描述 |
---|---|
dayOfWeekInMonth | 返回同一個(gè)月中每周的第幾天 |
firstDayOfMonth | 返回當(dāng)月的第一天 |
firstDayOfNextMonth | 返回下月的第一天 |
firstDayOfNextYear | 返回下一年的第一天 |
firstDayOfYear | 返回本年的第一天 |
firstInMonth | 返回同一個(gè)月中第一個(gè)星期幾 |
lastDayOfMonth | 返回當(dāng)月的最后一天 |
lastDayOfNextMonth | 返回下月的最后一天 |
lastDayOfNextYear | 返回下一年的最后一天 |
lastDayOfYear | 返回本年的最后一天 |
lastInMonth | 返回同一個(gè)月中最后一個(gè)星期幾 |
next / previous | 返回后一個(gè)/前一個(gè)給定的星期幾 |
nextOrSame / previousOrSame | 返回后一個(gè)/前一個(gè)給定的星期幾众辨,如果這個(gè)值滿足條件端三,直接返回 |
格式化時(shí)間
LocalDate localDate = LocalDate.of(2019, 9, 12);
String s1 = localDate.format(DateTimeFormatter.BASIC_ISO_DATE);
String s2 = localDate.format(DateTimeFormatter.ISO_LOCAL_DATE);
// 自定義格式化
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String s3 = localDate.format(dateTimeFormatter);
解析時(shí)間
LocalDate localDate1 = LocalDate.parse("20190912", DateTimeFormatter.BASIC_ISO_DATE);
LocalDate localDate2 = LocalDate.parse("2019-09-12", DateTimeFormatter.ISO_LOCAL_DATE);
總結(jié)
和 SimpleDateFormat 相比,DateTimeFormatter 是線程安全的泻轰。
Instant 的精確度更高且轨,可以精確到納秒級。
Duration 可以便捷得到時(shí)間段內(nèi)的天數(shù)旋奢、小時(shí)數(shù)等泳挥。
LocalDateTime 能夠快速地獲取年、月至朗、日屉符、下一月等。
TemporalAdjusters 類中包含許多常用的靜態(tài)方法唆香,避免自己編寫工具類吨艇。
歡迎關(guān)注我的公眾號:武培軒,獲得獨(dú)家整理的學(xué)習(xí)資源和日常干貨推送东涡。