摘要:什么是在對的解釋如下是一個用來對位置敏感的格式化和解析日期的實體類。他允許把日期格式化成,把解析成日期和規(guī)范化。
1.什么是SimpleDateFormat
在java doc對SimpleDateFormat的解釋如下:
SimpleDateFormat is a concrete class for formatting and parsing dates in a locale-sensitive manner. It allows for formatting
(date → text), parsing (text → date), and normalization.
SimpleDateFormat是一個用來對位置敏感的格式化和解析日期的實體類。他允許把日期格式化成text,把text解析成日期和規(guī)范化。
1.1使用SimpleDateFormatsimpleDateFormat的使用方法比較簡單:
public static void main(String[] args) throws Exception { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss"); System.out.println(simpleDateFormat.format(new Date())); System.out.println(simpleDateFormat.parse("2018-07-09 11:10:21")); }
1.首先需要定義一個日期的pattern,這里我們定義的是"yyyy-mm-dd HH:mm:ss" ,也就是我們這個simpleDateFormat不管是格式化還是解析都需要按照這個pattern。
2.對于format需要傳遞Date的對象,會返回一個String類型,這個String會按照我們上面的格式生成。
3.對于parse需要傳遞一個按照上面pattern的字符串,如果傳遞錯誤的pattern會拋出java.text.ParseException異常,如果傳遞正確的會生成一個Date對象。
附:格式占位符 G 年代標(biāo)志符 y 年 M 月 d 日 h 時 在上午或下午 (1~12) H 時 在一天中 (0~23) m 分 s 秒 S 毫秒 E 星期 D 一年中的第幾天 F 一月中第幾個星期幾 w 一年中第幾個星期 W 一月中第幾個星期 a 上午 / 下午 標(biāo)記符 k 時 在一天中 (1~24) K 時 在上午或下午 (0~11) z 時區(qū)2.SimpleDateFormat的隱患
很多初學(xué)者,或者一些經(jīng)驗比較淺的java開發(fā)工程師,用SimpleDateFormat會出現(xiàn)一些奇奇怪怪的BUG。
1.結(jié)果值不對:轉(zhuǎn)換的結(jié)果值經(jīng)常會出人意料,和預(yù)期不同,往往讓很多人摸不著頭腦。
2.內(nèi)存泄漏: 由于轉(zhuǎn)換的結(jié)果值不對,后續(xù)的一些操作,如一個循環(huán),累加一天處理一個東西,但是生成的日期如果異常導(dǎo)致很大的話,會讓這個循環(huán)變成一個類似死循環(huán)一樣導(dǎo)致系統(tǒng)內(nèi)存泄漏,頻繁觸發(fā)GC,造成系統(tǒng)不可用。
為什么會出現(xiàn)這么多問題呢?因為SimpleDateFormat線程不安全,很多人都會寫個Util類,然后把SimpleDateFormat定義成全局的一個常量,所有線程都共享這個常量:
protected static final SimpleDateFormat dayFormat = new SimpleDateFormat("yyyy-MM-dd"); public static Date formatDate(String date) throws ParseException { return dayFormat.parse(date); }
為什么SimpleDateFormat會線程不安全呢,在SimpleDateFormat源碼中,所有的格式化和解析都需要通過一個中間對象進(jìn)行轉(zhuǎn)換,那就是Calendar,而這個也是我們出現(xiàn)線程不安全的罪魁禍?zhǔn)祝囅胍幌庐?dāng)我們有多個線程操作同一個Calendar的時候后來的線程會覆蓋先來線程的數(shù)據(jù),那最后其實返回的是后來線程的數(shù)據(jù),這樣就導(dǎo)致我們上面所述的BUG的產(chǎn)生:
/
/ Called from Format after creating a FieldDelegate 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; }3.如何避坑
對于SimpleDateFormat的解決方法有下面幾種:
3.1新建SimpleDateFormat上面出現(xiàn)Bug的原因是因為所有線程都共用一個SimpleDateFormat,這里有個比較好解決的辦法,每次使用的時候都創(chuàng)建一個新的SimpleDateFormat,我們可以在DateUtils中將創(chuàng)建SimpleDateFormat放在方法內(nèi)部:
public static Date formatDate(String date) throws ParseException { SimpleDateFormat dayFormat = new SimpleDateFormat("yyyy-MM-dd"); return dayFormat.parse(date); }
上面這個方法雖然能解決我們的問題但是引入了另外一個問題就是,如果這個方法使用量比較大,有可能會頻繁造成Young gc,整個系統(tǒng)還是會受一定的影響。
3.2使用ThreadLocal使用ThreadLocal能避免上面頻繁的造成Young gc,我們對每個線程都使用ThreadLocal進(jìn)行保存,由于ThreadLocal是線程之間隔離開的,所以不會出現(xiàn)線程安全問題:
private static ThreadLocal3.3使用第三方工具包simpleDateFormatThreadLocal = new ThreadLocal<>(); public static Date formatDate(String date) throws ParseException { SimpleDateFormat dayFormat = getSimpleDateFormat(); return dayFormat.parse(date); } ? private static SimpleDateFormat getSimpleDateFormat() { SimpleDateFormat simpleDateFormat = simpleDateFormatThreadLocal.get(); if (simpleDateFormat == null){ simpleDateFormat = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss") simpleDateFormatThreadLocal.set(simpleDateFormat); } return simpleDateFormat; }
雖然上面的ThreadLocal能解決我們出現(xiàn)的問題,但是第三方工具包提供的功能更加強(qiáng)大,在java中有兩個類庫比較出名一個是Joda-Time,一個是Apache common包
3.3.1 Joda-Time(推薦)Joda-Time 令時間和日期值變得易于管理、操作和理解。對于我們復(fù)雜的操作都可以使用Joda-Time操作,下面我列舉兩個例子,對于把日期加上90天,如果使用原生的Jdk我們需要這樣寫:
Calendar calendar = Calendar.getInstance(); calendar.set(2000, Calendar.JANUARY, 1, 0, 0, 0); SimpleDateFormat sdf = new SimpleDateFormat("E MM/dd/yyyy HH:mm:ss.SSS"); calendar.add(Calendar.DAY\_OF\_MONTH, 90); System.out.println(sdf.format(calendar.getTime()));
但是在我們的joda-time中只需要兩句話,并且api也比較通俗易懂,所以你為什么不用Joda-Time呢?
DateTime dateTime = new DateTime(2000, 1, 1, 0, 0, 0, 0); System.out.println(dateTime.plusDays(90).toString("E MM/dd/yyyy HH:mm:ss.SSS");3.3.2 common-lang包
在common-lang包中有個類叫FastDateFormat,由于common-lang這個包基本被很多Java項目都會引用,所以你可以不用專門去引用處理時間包,即可處理時間,在FastDateFormat中每次處理時間的時候會創(chuàng)建一個calendar,使用方法比較簡單代碼如下所示:
FastDateFormat.getInstance().format(new Date());
3.4升級jdk8(推薦)在java8中Date這個類中的很多方法包括構(gòu)造方法都被打上了@Deprecated廢棄的注解,取而代之的是LocalDateTime,LocalDate LocalTime這三個類:
LocalDate無法包含時間;
LocalTime無法包含日期;
LocalDateTime才能同時包含日期和時間。
如果你是Java8,那你一定要使用他,在日期的格式化和解析方面不用考慮線程安全性,代碼如下:
public static String formatTime(LocalDateTime time,String pattern) { return time.format(DateTimeFormatter.ofPattern(pattern)); }
?
當(dāng)然localDateTime是java8的一大亮點(diǎn),當(dāng)然不僅僅只是解決了線程安全的問題,同樣也提供了一些其他的運(yùn)算比如加減天數(shù):
//日期加上一個數(shù),根據(jù)field不同加不同值,field為ChronoUnit.* public static LocalDateTime plus(LocalDateTime time, long number, TemporalUnit field) { return time.plus(number, field); } ? //日期減去一個數(shù),根據(jù)field不同減不同值,field參數(shù)為ChronoUnit.* public static LocalDateTime minu(LocalDateTime time, long number, TemporalUnit field){ return time.minus(number,field); }
最后,如果你擔(dān)心使用LocalDateTime 會對你現(xiàn)有的代碼產(chǎn)生很大的改變的話,那你可以將他們兩進(jìn)行互轉(zhuǎn):
//Date轉(zhuǎn)換為LocalDateTime public static LocalDateTime convertDateToLDT(Date date) { return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); } ? //LocalDateTime轉(zhuǎn)換為Date public static Date convertLDTToDate(LocalDateTime time) { return Date.from(time.atZone(ZoneId.systemDefault()).toInstant()); }更多技術(shù)福利請掃我的技術(shù)公眾號
如果你喜歡這篇文章歡迎點(diǎn)贊,轉(zhuǎn)發(fā)。可以掃描公眾號即可獲得我的1v1 VIP服務(wù)。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/76417.html
摘要:方法可接收兩個參數(shù),第一個參數(shù)是分隔符,即用來分隔字符串的字符,默認(rèn)是所有的空字符,包括空格換行制表符等。拆分過程會消耗分隔符,所以拆分結(jié)果中不包含分隔符。 正如《你真的知道Python的字符串是什么嗎?》所寫,Python 中字符串是由 Uniocde 編碼的字符組成的不可變序列,它具備與其它序列共有的一些操作,例如判斷元素是否存在、拼接序列、切片操作、求長度、求最值、求元素的索引位...
摘要:方法可接收兩個參數(shù),第一個參數(shù)是分隔符,即用來分隔字符串的字符,默認(rèn)是所有的空字符,包括空格換行制表符等。拆分過程會消耗分隔符,所以拆分結(jié)果中不包含分隔符。 正如《你真的知道Python的字符串是什么嗎?》所寫,Python 中字符串是由 Uniocde 編碼的字符組成的不可變序列,它具備與其它序列共有的一些操作,例如判斷元素是否存在、拼接序列、切片操作、求長度、求最值、求元素的索引位...
摘要:合并日期和時間這個復(fù)合類名叫,是和的合體。截至目前,我們介紹的這些日期時間對象都是不可修改的,這是為了更好地支持函數(shù)式編程,確保線程安全,保持領(lǐng)域模式一致性而做出的重大設(shè)計決定。 新的日期和時間API Java的API提供了很多有用的組件,能幫助你構(gòu)建復(fù)雜的應(yīng)用。不過,Java API也不總是完美的。我們相信大多數(shù)有經(jīng)驗的程序員都會贊同Java 8之前的庫對日期和時間的支持就非常不理想...
閱讀 2538·2021-10-12 10:12
閱讀 1720·2019-08-30 15:52
閱讀 2454·2019-08-30 13:04
閱讀 1741·2019-08-29 18:33
閱讀 967·2019-08-29 16:28
閱讀 454·2019-08-29 12:33
閱讀 2062·2019-08-26 13:33
閱讀 2366·2019-08-26 11:36