萬字博文教你搞懂java源碼的日期和時間相關用法(1)

      網友投稿 874 2025-04-02

      介紹

      本篇文章主要介紹java源碼中提供了哪些日期和時間的類

      日期和時間的兩套API

      java提供了兩套處理日期和時間的API

      1、舊的API,放在java.util 這個包下的:比較常用的有Date和Calendar等

      2、新的API是java 8新引入的,放在java.time 這個包下的:LocalDateTime,ZonedDateTime,DateTimeFormatter和Instant等

      為什么會有兩套日期時間API,這個是有歷史原因的,舊的API是jdk剛開始就提供的,隨著版本的升級,逐漸發(fā)現原先的api不滿足需要,暴露了一些問題,所以在java 8 這個版本中,重新引入新API。

      這兩套API都要了解,為什么呢?

      因為java 8 發(fā)布時間是2014年,很多之前的系統(tǒng)還是沿用舊的API,所以這兩套API都要了解,同時還要掌握兩套API相互轉化的技術。

      一:Date

      支持版本及以上

      JDK1.0

      介紹

      Date類說明

      Date類負責時間的表示,在計算機中,時間的表示是一個較大的概念,現有的系統(tǒng)基本都是利用從1970.1.1 00:00:00 到當前時間的毫秒數進行計時,這個時間稱為epoch(時間戳)

      package java.util; public class Date implements java.io.Serializable, Cloneable, Comparable { ... private transient long fastTime; .... }

      java.util.Date是java提供表示日期和時間的類,類里有個long 類型的變量fastTime,它是用來存儲以毫秒表示的時間戳。

      date常用的用法

      import java.util.Date; ----------------------------------------- //獲取當前時間 Date date = new Date(); System.out.println("獲取當前時間:"+date); //獲取時間戳 System.out.println("獲取時間戳:"+date.getTime()); // date時間是否大于afterDate 等于也為false Date afterDate = new Date(date.getTime()-3600*24*1000); System.out.println("after:"+date.after(afterDate)); System.out.println("after:"+date.after(date)); // date時間是否小于afterDate 等于也為false Date beforeDate = new Date(date.getTime()+3600*24*1000); System.out.println("before:"+date.before(beforeDate)); System.out.println("before:"+date.before(date)); //兩個日期比較 System.out.println("compareTo:"+date.compareTo(date)); System.out.println("compareTo:"+date.compareTo(afterDate)); System.out.println("compareTo:"+date.compareTo(beforeDate)); //轉為字符串 System.out.println("轉為字符串:"+date.toString()); //轉為GMT時區(qū) toGMTString() java8 中已廢棄 System.out.println("轉為GMT時區(qū):"+date.toGMTString()); //轉為本地時區(qū) toLocaleString() java8 已廢棄 System.out.println("轉為本地時區(qū):"+date.toLocaleString());

      自定義時間格式-SimpleDateFormat

      date的toString方法轉成字符串,不是我們想要的時間格式,如果要自定義時間格式,就要使用SimpleDateFormat

      //獲取當前時間 Date date = new Date(); System.out.println("獲取當前時間:"+date); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println(simpleDateFormat.format(date)); SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("yyyy年MM月dd日 HH時mm分ss秒"); System.out.println(simpleDateFormat1.format(date));

      SimpleDateFormat也可以方便的將字符串轉成Date

      //獲取當前時間 String str = "2021-07-13 23:48:23"; try { Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(str); System.out.println(date); } catch (ParseException e) { e.printStackTrace(); }

      日期和時間格式化參數說明

      yyyy:年 MM:月 dd:日 hh:1~12小時制(1-12) HH:24小時制(0-23) mm:分 ss:秒 S:毫秒 E:星期幾 D:一年中的第幾天 F:一月中的第幾個星期(會把這個月總共過的天數除以7) w:一年中的第幾個星期 W:一月中的第幾星期(會根據實際情況來算) a:上下午標識 k:和HH差不多,表示一天24小時制(1-24)。 K:和hh差不多,表示一天12小時制(0-11)。 z:表示時區(qū)

      SimpleDateFormat線程不安全原因及解決方案

      來看看SimpleDateFormat的源碼,先看format方法:

      // 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); ... }

      問題就出在成員變量calendar,如果在使用SimpleDateFormat時,用static定義,那SimpleDateFormat變成了共享變量。那SimpleDateFormat中的calendar就可以被多個線程訪問到。

      SimpleDateFormat的parse方法也是線程不安全的:

      public Date parse(String text, ParsePosition pos) { ... Date parsedDate; try { parsedDate = calb.establish(calendar).getTime(); // If the year value is ambiguous, // then the two-digit year == the default start year if (ambiguousYear[0]) { if (parsedDate.before(defaultCenturyStart)) { parsedDate = calb.addYear(100).establish(calendar).getTime(); } } } // An IllegalArgumentException will be thrown by Calendar.getTime() // if any fields are out of range, e.g., MONTH == 17. catch (IllegalArgumentException e) { pos.errorIndex = start; pos.index = oldStart; return null; } return parsedDate; }

      由源碼可知,最后是調用**parsedDate = calb.establish(calendar).getTime();**獲取返回值。方法的參數是calendar,calendar可以被多個線程訪問到,存在線程不安全問題。

      我們再來看看**calb.establish(calendar)**的源碼

      calb.establish(calendar)方法先后調用了cal.clear()和cal.set(),先清理值,再設值。但是這兩個操作并不是原子性的,也沒有線程安全機制來保證,導致多線程并發(fā)時,可能會引起cal的值出現問題了。

      public class SimpleDateFormatDemoTest { private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static void main(String[] args) { //1、創(chuàng)建線程池 ExecutorService pool = Executors.newFixedThreadPool(5); //2、為線程池分配任務 ThreadPoolTest threadPoolTest = new ThreadPoolTest(); for (int i = 0; i < 10; i++) { pool.submit(threadPoolTest); } //3、關閉線程池 pool.shutdown(); } static class ThreadPoolTest implements Runnable{ @Override public void run() { String dateString = simpleDateFormat.format(new Date()); try { Date parseDate = simpleDateFormat.parse(dateString); String dateString2 = simpleDateFormat.format(parseDate); System.out.println(Thread.currentThread().getName()+" 線程是否安全: "+dateString.equals(dateString2)); } catch (Exception e) { System.out.println(Thread.currentThread().getName()+" 格式化失敗 "); } } } }

      出現了兩次false,說明線程是不安全的。而且還拋異常,這個就嚴重了。

      這個是阿里巴巴 java開發(fā)手冊中的規(guī)定:

      1、不要定義為static變量,使用局部變量

      2、加鎖:synchronized鎖和Lock鎖

      3、使用ThreadLocal方式

      4、使用DateTimeFormatter代替SimpleDateFormat(DateTimeFormatter是線程安全的,java 8+支持)

      5、使用FastDateFormat 替換SimpleDateFormat(FastDateFormat 是線程安全的,Apache Commons Lang包支持,不受限于java版本)

      就是要使用SimpleDateFormat對象進行format或parse時,再定義為局部變量。就能保證線程安全。

      public class SimpleDateFormatDemoTest1 { public static void main(String[] args) { //1、創(chuàng)建線程池 ExecutorService pool = Executors.newFixedThreadPool(5); //2、為線程池分配任務 ThreadPoolTest threadPoolTest = new ThreadPoolTest(); for (int i = 0; i < 10; i++) { pool.submit(threadPoolTest); } //3、關閉線程池 pool.shutdown(); } static class ThreadPoolTest implements Runnable{ @Override public void run() { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateString = simpleDateFormat.format(new Date()); try { Date parseDate = simpleDateFormat.parse(dateString); String dateString2 = simpleDateFormat.format(parseDate); System.out.println(Thread.currentThread().getName()+" 線程是否安全: "+dateString.equals(dateString2)); } catch (Exception e) { System.out.println(Thread.currentThread().getName()+" 格式化失敗 "); } } } }

      由圖可知,已經保證了線程安全,但這種方案不建議在高并發(fā)場景下使用,因為會創(chuàng)建大量的SimpleDateFormat對象,影響性能。

      加synchronized鎖:

      萬字博文教你搞懂java源碼的日期和時間相關用法(1)

      SimpleDateFormat對象還是定義為全局變量,然后需要調用SimpleDateFormat進行格式化時間時,再用synchronized保證線程安全。

      public class SimpleDateFormatDemoTest2 { private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static void main(String[] args) { //1、創(chuàng)建線程池 ExecutorService pool = Executors.newFixedThreadPool(5); //2、為線程池分配任務 ThreadPoolTest threadPoolTest = new ThreadPoolTest(); for (int i = 0; i < 10; i++) { pool.submit(threadPoolTest); } //3、關閉線程池 pool.shutdown(); } static class ThreadPoolTest implements Runnable{ @Override public void run() { try { synchronized (simpleDateFormat){ String dateString = simpleDateFormat.format(new Date()); Date parseDate = simpleDateFormat.parse(dateString); String dateString2 = simpleDateFormat.format(parseDate); System.out.println(Thread.currentThread().getName()+" 線程是否安全: "+dateString.equals(dateString2)); } } catch (Exception e) { System.out.println(Thread.currentThread().getName()+" 格式化失敗 "); } } } }

      如圖所示,線程是安全的。定義了全局變量SimpleDateFormat,減少了創(chuàng)建大量SimpleDateFormat對象的損耗。但是使用synchronized鎖,

      同一時刻只有一個線程能執(zhí)行鎖住的代碼塊,在高并發(fā)的情況下會影響性能。但這種方案不建議在高并發(fā)場景下使用

      加Lock鎖:

      加Lock鎖和synchronized鎖原理是一樣的,都是使用鎖機制保證線程的安全。

      public class SimpleDateFormatDemoTest3 { private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private static Lock lock = new ReentrantLock(); public static void main(String[] args) { //1、創(chuàng)建線程池 ExecutorService pool = Executors.newFixedThreadPool(5); //2、為線程池分配任務 ThreadPoolTest threadPoolTest = new ThreadPoolTest(); for (int i = 0; i < 10; i++) { pool.submit(threadPoolTest); } //3、關閉線程池 pool.shutdown(); } static class ThreadPoolTest implements Runnable{ @Override public void run() { try { lock.lock(); String dateString = simpleDateFormat.format(new Date()); Date parseDate = simpleDateFormat.parse(dateString); String dateString2 = simpleDateFormat.format(parseDate); System.out.println(Thread.currentThread().getName()+" 線程是否安全: "+dateString.equals(dateString2)); } catch (Exception e) { System.out.println(Thread.currentThread().getName()+" 格式化失敗 "); }finally { lock.unlock(); } } } }

      由結果可知,加Lock鎖也能保證線程安全。要注意的是,最后一定要釋放鎖,代碼里在finally里增加了lock.unlock();,保證釋放鎖。

      在高并發(fā)的情況下會影響性能。這種方案不建議在高并發(fā)場景下使用

      使用ThreadLocal保證每一個線程有SimpleDateFormat對象副本。這樣就能保證線程的安全。

      public class SimpleDateFormatDemoTest4 { private static ThreadLocal threadLocal = new ThreadLocal(){ @Override protected DateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); } }; public static void main(String[] args) { //1、創(chuàng)建線程池 ExecutorService pool = Executors.newFixedThreadPool(5); //2、為線程池分配任務 ThreadPoolTest threadPoolTest = new ThreadPoolTest(); for (int i = 0; i < 10; i++) { pool.submit(threadPoolTest); } //3、關閉線程池 pool.shutdown(); } static class ThreadPoolTest implements Runnable{ @Override public void run() { try { String dateString = threadLocal.get().format(new Date()); Date parseDate = threadLocal.get().parse(dateString); String dateString2 = threadLocal.get().format(parseDate); System.out.println(Thread.currentThread().getName()+" 線程是否安全: "+dateString.equals(dateString2)); } catch (Exception e) { System.out.println(Thread.currentThread().getName()+" 格式化失敗 "); }finally { //避免內存泄漏,使用完threadLocal后要調用remove方法清除數據 threadLocal.remove(); } } } }

      使用ThreadLocal能保證線程安全,且效率也是挺高的。適合高并發(fā)場景使用。

      使用DateTimeFormatter代替SimpleDateFormat(DateTimeFormatter是線程安全的,java 8+支持)

      DateTimeFormatter介紹

      public class DateTimeFormatterDemoTest5 { private static DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); public static void main(String[] args) { //1、創(chuàng)建線程池 ExecutorService pool = Executors.newFixedThreadPool(5); //2、為線程池分配任務 ThreadPoolTest threadPoolTest = new ThreadPoolTest(); for (int i = 0; i < 10; i++) { pool.submit(threadPoolTest); } //3、關閉線程池 pool.shutdown(); } static class ThreadPoolTest implements Runnable{ @Override public void run() { try { String dateString = dateTimeFormatter.format(LocalDateTime.now()); TemporalAccessor temporalAccessor = dateTimeFormatter.parse(dateString); String dateString2 = dateTimeFormatter.format(temporalAccessor); System.out.println(Thread.currentThread().getName()+" 線程是否安全: "+dateString.equals(dateString2)); } catch (Exception e) { e.printStackTrace(); System.out.println(Thread.currentThread().getName()+" 格式化失敗 "); } } } }

      使用DateTimeFormatter能保證線程安全,且效率也是挺高的。適合高并發(fā)場景使用。

      使用FastDateFormat 替換SimpleDateFormat(FastDateFormat 是線程安全的,Apache Commons Lang包支持,不受限于java版本)

      public class FastDateFormatDemo6 { private static FastDateFormat fastDateFormat = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss"); public static void main(String[] args) { //1、創(chuàng)建線程池 ExecutorService pool = Executors.newFixedThreadPool(5); //2、為線程池分配任務 ThreadPoolTest threadPoolTest = new ThreadPoolTest(); for (int i = 0; i < 10; i++) { pool.submit(threadPoolTest); } //3、關閉線程池 pool.shutdown(); } static class ThreadPoolTest implements Runnable{ @Override public void run() { try { String dateString = fastDateFormat.format(new Date()); Date parseDate = fastDateFormat.parse(dateString); String dateString2 = fastDateFormat.format(parseDate); System.out.println(Thread.currentThread().getName()+" 線程是否安全: "+dateString.equals(dateString2)); } catch (Exception e) { e.printStackTrace(); System.out.println(Thread.currentThread().getName()+" 格式化失敗 "); } } } }

      使用FastDateFormat能保證線程安全,且效率也是挺高的。適合高并發(fā)場景使用。

      Apache Commons Lang 3.5

      //FastDateFormat @Override public String format(final Date date) { return printer.format(date); } @Override public String format(final Date date) { final Calendar c = Calendar.getInstance(timeZone, locale); c.setTime(date); return applyRulesToString(c); }

      源碼中 Calender 是在 format 方法里創(chuàng)建的,肯定不會出現 setTime 的線程安全問題。這樣線程安全疑惑解決了。那還有性能問題要考慮?

      我們來看下FastDateFormat是怎么獲取的

      FastDateFormat.getInstance(); FastDateFormat.getInstance(CHINESE_DATE_TIME_PATTERN);

      看下對應的源碼

      /** * 獲得 FastDateFormat實例,使用默認格式和地區(qū) * * @return FastDateFormat */ public static FastDateFormat getInstance() { return CACHE.getInstance(); } /** * 獲得 FastDateFormat 實例,使用默認地區(qū)
      * 支持緩存 * * @param pattern 使用{@link java.text.SimpleDateFormat} 相同的日期格式 * @return FastDateFormat * @throws IllegalArgumentException 日期格式問題 */ public static FastDateFormat getInstance(final String pattern) { return CACHE.getInstance(pattern, null, null); }

      這里有用到一個CACHE,看來用了緩存,往下看

      private static final FormatCache CACHE = new FormatCache(){ @Override protected FastDateFormat createInstance(final String pattern, final TimeZone timeZone, final Locale locale) { return new FastDateFormat(pattern, timeZone, locale); } }; // abstract class FormatCache { ... private final ConcurrentMap cInstanceCache = new ConcurrentHashMap<>(7); private static final ConcurrentMap C_DATE_TIME_INSTANCE_CACHE = new ConcurrentHashMap<>(7); ... }

      在getInstance 方法中加了ConcurrentMap 做緩存,提高了性能。且我們知道ConcurrentMap 也是線程安全的。

      /** * 年月格式 {@link FastDateFormat}:yyyy-MM */ public static final FastDateFormat NORM_MONTH_FORMAT = FastDateFormat.getInstance(NORM_MONTH_PATTERN);

      //FastDateFormat public static FastDateFormat getInstance(final String pattern) { return CACHE.getInstance(pattern, null, null); }

      如圖可證,是使用了ConcurrentMap 做緩存。且key值是格式,時區(qū)和locale(語境)三者都相同為相同的key。

      問題

      1、tostring()輸出時,總以系統(tǒng)的默認時區(qū)格式輸出,不友好。

      2、時區(qū)不能轉換

      3、日期和時間的計算不簡便,例如計算加減,比較兩個日期差幾天等。

      4、格式化日期和時間的SimpleDateFormat對象是線程不安全的

      5、Date對象本身也是線程不安全的

      public class Date implements java.io.Serializable, Cloneable, Comparable { ... }

      二:Calendar

      支持版本及以上

      JDK1.1

      介紹

      Calendar類說明

      Calendar類提供了獲取或設置各種日歷字段的各種方法,比Date類多了一個可以計算日期和時間的功能。

      Calendar常用的用法

      // 獲取當前時間: Calendar c = Calendar.getInstance(); int y = c.get(Calendar.YEAR); int m = 1 + c.get(Calendar.MONTH); int d = c.get(Calendar.DAY_OF_MONTH); int w = c.get(Calendar.DAY_OF_WEEK); int hh = c.get(Calendar.HOUR_OF_DAY); int mm = c.get(Calendar.MINUTE); int ss = c.get(Calendar.SECOND); int ms = c.get(Calendar.MILLISECOND); System.out.println("返回的星期:"+w); System.out.println(y + "-" + m + "-" + d + " " + " " + hh + ":" + mm + ":" + ss + "." + ms);

      如上圖所示,月份計算時,要+1;返回的星期是從周日開始計算,周日為1,1~7表示星期;

      Calendar的跨年問題和解決方案

      背景:在使用Calendar 的api getWeekYear()讀取年份,在跨年那周的時候,程序獲取的年份可能不是我們想要的,例如在2019年30號時,要返回2019,結果是返回2020,是不是有毒

      // 獲取當前時間: Calendar c = Calendar.getInstance(); c.clear(); String str = "2019-12-30"; try { c.setTime(new SimpleDateFormat("yyyy-MM-dd").parse(str)); int y = c.getWeekYear(); System.out.println(y); } catch (ParseException e) { e.printStackTrace(); }

      老規(guī)矩,從源碼入手

      Calendar類 ------------------------- //@since 1.7 public int getWeekYear() { throw new UnsupportedOperationException(); }

      這個源碼有點奇怪,getWeekYear()方法是java 7引入的。它的實現怎么是拋出異常,但是執(zhí)行時,又有結果返回。

      斷點跟進,通過Calendar.getInstance()獲取的Calendar實例是GregorianCalendar

      GregorianCalendar -------------------------------------- public int getWeekYear() { int year = get(YEAR); // implicitly calls complete() if (internalGetEra() == BCE) { year = 1 - year; } // Fast path for the Gregorian calendar years that are never // affected by the Julian-Gregorian transition if (year > gregorianCutoverYear + 1) { int weekOfYear = internalGet(WEEK_OF_YEAR); if (internalGet(MONTH) == JANUARY) { if (weekOfYear >= 52) { --year; } } else { if (weekOfYear == 1) { ++year; } } return year; } ... }

      方法內獲取的年份剛開始是正常的

      在JDK中會把前一年末尾的幾天判定為下一年的第一周,因此上面程序的結果是1

      使用Calendar類 get(Calendar.YEAR)獲取年份

      問題

      1、讀取月份時,要+1

      2、返回的星期是從周日開始計算,周日為1,1~7表示星期

      3、Calendar的跨年問題,獲取年份要用c.get(Calendar.YEAR),不要用c.getWeekYear();

      4、獲取指定時間是一年中的第幾周時,調用cl.get(Calendar.WEEK_OF_YEAR),要注意跨年問題,跨年的那一周,獲取的值為1。離跨年最近的那周為52。

      三:LocalDateTime

      支持版本及以上

      jdk8

      介紹

      LocalDateTime類說明

      表示當前日期時間,相當于:yyyy-MM-ddTHH:mm:ss

      LocalDateTime常用的用法

      LocalDate d = LocalDate.now(); // 當前日期 LocalTime t = LocalTime.now(); // 當前時間 LocalDateTime dt = LocalDateTime.now(); // 當前日期和時間 System.out.println(d); // 嚴格按照ISO 8601格式打印 System.out.println(t); // 嚴格按照ISO 8601格式打印 System.out.println(dt); // 嚴格按照ISO 8601格式打印

      由運行結果可行,本地日期時間通過now()獲取到的總是以當前默認時區(qū)返回的

      LocalDate d2 = LocalDate.of(2021, 07, 14); // 2021-07-14, 注意07=07月 LocalTime t2 = LocalTime.of(13, 14, 20); // 13:14:20 LocalDateTime dt2 = LocalDateTime.of(2021, 07, 14, 13, 14, 20); LocalDateTime dt3 = LocalDateTime.of(d2, t2); System.out.println("指定日期時間:"+dt2); System.out.println("指定日期時間:"+dt3);

      LocalDateTime currentTime = LocalDateTime.now(); // 當前日期和時間 System.out.println("------------------時間的加減法及修改-----------------------"); //3.LocalDateTime的加減法包含了LocalDate和LocalTime的所有加減,上面說過,這里就只做簡單介紹 System.out.println("3.當前時間:" + currentTime); System.out.println("3.當前時間加5年:" + currentTime.plusYears(5)); System.out.println("3.當前時間加2個月:" + currentTime.plusMonths(2)); System.out.println("3.當前時間減2天:" + currentTime.minusDays(2)); System.out.println("3.當前時間減5個小時:" + currentTime.minusHours(5)); System.out.println("3.當前時間加5分鐘:" + currentTime.plusMinutes(5)); System.out.println("3.當前時間加20秒:" + currentTime.plusSeconds(20)); //還可以靈活運用比如:向后加一年,向前減一天,向后加2個小時,向前減5分鐘,可以進行連寫 System.out.println("3.同時修改(向后加一年,向前減一天,向后加2個小時,向前減5分鐘):" + currentTime.plusYears(1).minusDays(1).plusHours(2).minusMinutes(5)); System.out.println("3.修改年為2025年:" + currentTime.withYear(2025)); System.out.println("3.修改月為12月:" + currentTime.withMonth(12)); System.out.println("3.修改日為27日:" + currentTime.withDayOfMonth(27)); System.out.println("3.修改小時為12:" + currentTime.withHour(12)); System.out.println("3.修改分鐘為12:" + currentTime.withMinute(12)); System.out.println("3.修改秒為12:" + currentTime.withSecond(12));

      LocalDateTime和Date相互轉化

      System.out.println("------------------方法一:分步寫-----------------------"); //實例化一個時間對象 Date date = new Date(); //返回表示時間軸上同一點的瞬間作為日期對象 Instant instant = date.toInstant(); //獲取系統(tǒng)默認時區(qū) ZoneId zoneId = ZoneId.systemDefault(); //根據時區(qū)獲取帶時區(qū)的日期和時間 ZonedDateTime zonedDateTime = instant.atZone(zoneId); //轉化為LocalDateTime LocalDateTime localDateTime = zonedDateTime.toLocalDateTime(); System.out.println("方法一:原Date = " + date); System.out.println("方法一:轉化后的LocalDateTime = " + localDateTime); System.out.println("------------------方法二:一步到位(推薦使用)-----------------------"); //實例化一個時間對象 Date todayDate = new Date(); //Instant.ofEpochMilli(long l)使用1970-01-01T00:00:00Z的紀元中的毫秒來獲取Instant的實例 LocalDateTime ldt = Instant.ofEpochMilli(todayDate.getTime()).atZone(ZoneId.systemDefault()).toLocalDateTime(); System.out.println("方法二:原Date = " + todayDate); System.out.println("方法二:轉化后的LocalDateTime = " + ldt);

      System.out.println("------------------方法一:分步寫-----------------------"); //獲取LocalDateTime對象,當前時間 LocalDateTime localDateTime = LocalDateTime.now(); //獲取系統(tǒng)默認時區(qū) ZoneId zoneId = ZoneId.systemDefault(); //根據時區(qū)獲取帶時區(qū)的日期和時間 ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId); //返回表示時間軸上同一點的瞬間作為日期對象 Instant instant = zonedDateTime.toInstant(); //轉化為Date Date date = Date.from(instant); System.out.println("方法一:原LocalDateTime = " + localDateTime); System.out.println("方法一:轉化后的Date = " + date); System.out.println("------------------方法二:一步到位(推薦使用)-----------------------"); //實例化一個LocalDateTime對象 LocalDateTime now = LocalDateTime.now(); //轉化為date Date dateResult = Date.from(now.atZone(ZoneId.systemDefault()).toInstant()); System.out.println("方法二:原LocalDateTime = " + now); System.out.println("方法二:轉化后的Date = " + dateResult);

      線程安全

      網上大家都在說JAVA 8提供的LocalDateTime是線程安全的,但是它是如何實現的呢

      今天讓我們來挖一挖

      public final class LocalDateTime implements Temporal, TemporalAdjuster, ChronoLocalDateTime, Serializable { ... }

      由上面的源碼可知,LocalDateTime是不可變類。我們都知道一個java并發(fā)編程規(guī)則:不可變對象永遠是線程安全的。

      對比下Date的源碼 ,Date是可變類,所以是線程不安全的。

      public class Date implements java.io.Serializable, Cloneable, Comparable { ... }

      Java JDK

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發(fā)現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發(fā)現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:2007excel表格怎么設置打印區(qū)域
      下一篇:WPS表格用怎么IS類函數來優(yōu)化公式(wps表格函數公式大全及使用方法)
      相關文章
      亚洲欧美日韩中文无线码 | 亚洲一二成人精品区| 亚洲六月丁香婷婷综合| 亚洲不卡中文字幕无码| 久久精品国产亚洲麻豆| 久久精品国产精品亚洲精品| 亚洲国产精品嫩草影院在线观看 | 久久精品国产亚洲AV蜜臀色欲 | 久久亚洲高清综合| 亚洲成年人在线观看| 亚洲国产综合精品中文字幕| 久久亚洲精品成人无码网站| 亚洲成a人一区二区三区| 亚洲日本中文字幕天天更新| 亚洲人成人77777网站不卡| 亚洲一级免费毛片| 亚洲综合小说另类图片动图 | 亚洲VA中文字幕无码毛片| 亚洲jjzzjjzz在线播放| 亚洲国产综合在线| 亚洲精品福利在线观看| 亚洲国产高清视频| 久久亚洲精品专区蓝色区| 亚洲性色精品一区二区在线| 久久精品国产亚洲AV无码娇色 | 亚洲国产精品一区二区三区在线观看| 国产成人精品日本亚洲专| 亚洲一本一道一区二区三区| 性xxxx黑人与亚洲| 在线a亚洲老鸭窝天堂av高清| 亚洲欧美国产日韩av野草社区| 亚洲第一se情网站| 亚洲欧美日韩综合久久久久 | 亚洲精品无码高潮喷水A片软| 亚洲人成自拍网站在线观看| 精品国产日韩亚洲一区91| 亚洲神级电影国语版| 亚洲人成在线播放网站岛国| 亚洲视频在线观看视频| 国产亚洲精彩视频| 亚洲色精品三区二区一区|