2020 年,你還在使用 Java 中的 SimpleDateFormat 嗎?
2022-05-20由 CSDN 發表于 畜牧業
format方法怎麼用
作者 | 阿文
責編 | 屠敏
今天我和大家一起聊一聊SimpleDateFormat ,這都2020年了,怎麼還在用SimpleDateFormat ?
其實,作為一名Java 程式設計師,我們會經常在程式設計時候和時間打交道,比如要把某一個時間儲存到資料庫中去,而我們直接使用
Datedate = newDate();
System。out。println(date);
得到的時間都是這樣的
SunJun 07 17:22:52CST 2020
因此,我們經常需要把時間進行格式化處理,然後在進行儲存,方便閱讀。這個時候我們就會使用到SimpleDateFormat 類,比如使用下面的程式碼來獲取當前時間,並呼叫SimpleDateFormat 對時間進行格式化:
Date date = newDate();
SimpleDateFormat df = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);//設定日期格式
String datestr = df。format(date);
System。out。println(datestr);
最終輸出的時間為
2020-06-07 16:45:58
由於在java 8之前 SimpleDateFormat 是一個比較常用的類,但是我還是在這裡要建議開發者不要用 SimpleDateForma。原因有兩點:
首先,透過new 一個物件來操作物件,佔用記憶體大
,如果每處理一個時間資訊的時候,就需要new一個SimpleDateFormat例項物件,然後再丟棄這個物件。大量的物件就這樣被創建出來,佔用大量的記憶體和 jvm空間。
那麼很多人就會想,既然new 代價太大, 不如我們使用 static 將其設定為共享變數,這樣就可以減小頻繁建立物件帶來的記憶體開銷啦,真的是機智如我。
privatestatic SimpleDateFormat simpleDateFormat = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);
但是這樣操作在併發量非常大的情況下,由於 SimpleDateFormat 是執行緒不安全的,這也是第二點原因,這個在JDK文件中已經明確表明了SimpleDateFormat不應該用在多執行緒場景中:
Date formats are not synchronized。 It is recommended to create separate format instances for each thread。 If multiple threads access a format concurrently, it must be synchronized externally。
在《阿里巴巴 Java 開發手冊》中也明確表示不要定義SimpleDateFormat 為static 變數,如果定義static 必須要加鎖。
這背後的原因是由於 SimpleDateFormat 中的format方法在執行過程中,會使用一個成員變數calendar來儲存時間。
private StringBuffer format(Date date, StringBuffer toAppendTo, FieldDelegate delegate) {
this。calendar。setTime(date);
boolean useDateFormatSymbols = this。useDateFormatSymbols();
int i = 0;
……
由於在宣告SimpleDateFormat的時候,使用的是static定義的。那麼這個SimpleDateFormat就是一個共享變數,SimpleDateFormat 中的calendar也就可以被多個執行緒訪問到。
舉個例子:假設一個執行緒A剛執行完calendar。setTime把時間設定成2020-05-07,這個執行緒還沒執行完,執行緒B又執行了calendar。setTime把時間改成了2020-06-07。這時候執行緒A繼續往下執行,拿到的calendar。getTime得到的時間就是執行緒B改過之後的。
除了format方法以外,SimpleDateFormat的parse方法也有同樣的問題。
所以,不要把SimpleDateFormat作為一個共享變數使用。
那麼如何解決這樣的問題呢?如果你使用的是Java 8 之前的JDK,那麼上面的《阿里巴巴Java 開發手冊》已經給出瞭解決方案,那就是使SimpleDateFormat 變成執行緒安全的,透過加鎖的方式來解決
privatestatic ThreadLocal
@Override
protected DateFormat initialValue(){
returnnew SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);
}
};
而如果你使用的是Java 8 + 的版本,那麼你完全可以拋棄這種執行緒不安全的時間格式化方法。可以使用DateTimeFormatter代替SimpleDateFormat,這是一個執行緒安全的格式化工具類。
LocalDate 和LocalDateTime
Java 8開始,明確了日期時間概念,例如:瞬時(instant)、 長短(duration)、日期、時間、時區和週期。
同時繼承了Joda 庫按人類語言和計算機各自解析的時間處理方式。不同於老版本,新API基於ISO標準日曆系統,java。time包下的所有類都是不可變型別而且執行緒安全。
關鍵類
Instant:瞬時例項。
LocalDate:本地日期,不包含具體時間 例如:2014-01-14 可以用來記錄生日、紀念日、加盟日等。
LocalTime:本地時間,不包含日期。
LocalDateTime:組合了日期和時間,但不包含時差和時區資訊。
ZonedDateTime:最完整的日期時間,包含時區和相對UTC或格林威治的時差。
新API還引入了 ZoneOffSet 和 ZoneId 類,使得解決時區問題更為簡便。解析、格式化時間的 DateTimeFormatter 類也全部重新設計。
例如,我們使用LocalDate 代替Date,使用DateTimeFormatter 代替SimpleDateFormat,如下所示:
String DateNow = LocalDateTime。now()。format(DateTimeFormatter。ofPattern(“yyyy/MM/dd HH:mm:ss”)); // 當前日期和時間
System。out。println(DateNow);
這樣就避免了SimpleDateFormat 的執行緒不安全問題啦。