阿里規定代碼中禁用static修飾SimpleDateFormat,為何?

2021-02-21 小黃鴨編程社區

在項目開發過程中經常遇到時間處理,但是你真的用對了嗎,理解阿里巴巴開發手冊中禁用static修飾SimpleDateFormat嗎?

通過閱讀本篇文章你將了解到:


# 為什麼需要LocalDate、LocalTime、LocalDateTime

1.Date如果不格式化,列印出的日期可讀性差

Tue Sep 10 09:34:04 CST 2019

2.使用SimpleDateFormat對時間進行格式化,但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是共享變量,並且這個共享變量沒有做線程安全控制。當多個線程同時使用相同的SimpleDateFormat對象【如用static修飾的SimpleDateFormat】調用format方法時,多個線程會同時調用calendar.setTime方法,可能一個線程剛設置好time值另外的一個線程馬上把設置的time值給修改了導致返回的格式化時間可能是錯誤的。在多並發情況下使用SimpleDateFormat需格外注意SimpleDateFormat除了format是線程不安全以外,parse方法也是線程不安全的。parse方法實際調用alb.establish(calendar).getTime()方法來解析,alb.establish(calendar)方法裡主要完成了

重置日期對象cal的屬性值

使用calb中中屬性設置cal

返回設置好的cal對象

但是這三步不是原子操作

多線程並發如何保證線程安全- 避免線程之間共享一個SimpleDateFormat對象,每個線程使用時都創建一次SimpleDateFormat對象 => 創建和銷毀對象的開銷大- 對使用format和parse方法的地方進行加鎖 => 線程阻塞性能差- 使用ThreadLocal保證每個線程最多只創建一次SimpleDateFormat對象 => 較好的方法


# Come On 一起使用java8全新的日期和時間API


LocalDate

只會獲取年月日

//獲取當前年月日LocalDate localDate = LocalDate.now();//構造指定的年月日LocalDate localDate1 = LocalDate.of(2019, 9, 10);

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

只會獲取幾點幾分幾秒

 LocalTime localTime = LocalTime.of(13, 51, 10); LocalTime localTime1 = LocalTime.now();

//獲取小時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.getSecond();int second1 = localTime.get(ChronoField.SECOND_OF_MINUTE);


LocalDateTime

獲取年月日時分秒,等於LocalDate+LocalTime

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 localDate2 = localDateTime.toLocalDate();

LocalTime localTime2 = localDateTime.toLocalTime();


Instant

獲取秒數

Instant instant = Instant.now();

long currentSecond = instant.getEpochSecond();

long currentMilli = instant.toEpochMilli();


個人覺得如果只是為了獲取秒數或者毫秒數,使用System.currentTimeMillis()來得更為方便

修改LocalDate、LocalTime、LocalDateTime、Instant

LocalDate、LocalTime、LocalDateTime、Instant為不可變對象,修改這些對象對象會返回一個副本

LocalDateTime localDateTime = LocalDateTime.of(2019, Month.SEPTEMBER, 10,              14, 46, 56);//增加一年localDateTime = localDateTime.plusYears(1);localDateTime = localDateTime.plus(1, ChronoUnit.YEARS);//減少一個月localDateTime = localDateTime.minusMonths(1);localDateTime = localDateTime.minus(1, ChronoUnit.MONTHS);

//修改年為2019localDateTime = localDateTime.withYear(2020);//修改為2022localDateTime = localDateTime.with(ChronoField.YEAR, 2022);

還可以修改月、日


時間計算

比如有些時候想知道這個月的最後一天是幾號、下個周末是幾號,通過提供的時間和日期API可以很快得到答案

LocalDate localDate = LocalDate.now();LocalDate localDate1 = localDate.with(firstDayOfYear());

比如通過firstDayOfYear()返回了當前日期的第一天日期,還有很多方法這裡不在舉例說明


格式化時間
LocalDate localDate = LocalDate.of(2019, 9, 10);String s1 = localDate.format(DateTimeFormatter.BASIC_ISO_DATE);String s2 = localDate.format(DateTimeFormatter.ISO_LOCAL_DATE);DateTimeFormatter dateTimeFormatter =   DateTimeFormatter.ofPattern("dd/MM/yyyy");String s3 = localDate.format(dateTimeFormatter);


DateTimeFormatter默認提供了多種格式化方式,如果默認提供的不能滿足要求,可以通過DateTimeFormatter的ofPattern方法創建自定義格式化方式


解析時間
LocalDate localDate1 = LocalDate.parse("20190910", DateTimeFormatter.BASIC_ISO_DATE);LocalDate localDate2 = LocalDate.parse("2019-09-10", DateTimeFormatter.ISO_LOCAL_DATE);

和SimpleDateFormat相比,DateTimeFormatter是線程安全的


# 小結


LocalDateTime:Date有的我都有,Date沒有的我也有,日期選擇請Pick Me。

SpringBoot中應用LocalDateTime

1.將LocalDateTime欄位以時間戳的方式返回給前端

添加日期轉化類

public class LocalDateTimeConverter extends JsonSerializer<LocalDateTime> {
@Override public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeNumber(value.toInstant(ZoneOffset.of("+8")).toEpochMilli()); }}


並在LocalDateTime欄位上添加@JsonSerialize(using = LocalDateTimeConverter.class)註解,如下:

@JsonSerialize(using = LocalDateTimeConverter.class)protected LocalDateTime gmtModified;


2.將LocalDateTime欄位以指定格式化日期的方式返回給前端
在LocalDateTime欄位上添加@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")註解即可,如下:

@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")protected LocalDateTime gmtModified;

3.對前端傳入的日期進行格式化
在LocalDateTime欄位上添加@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")註解即可,如下:

@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")protected LocalDateTime gmtModified;

作者:何甜甜在嗎
來源:https://juejin.im/post/5d7787625188252388753eae

相關焦點

  • 為什麼SimpleDateFormat不是線程安全的?
    = dateString;    }    @Override    public void run() {        try {            Date date = simpleDateFormat.parse(dateString);            String newDate = simpleDateFormat.format
  • Java SimpleDateFormat 沒那麼簡單
    (給ImportNew加星標,提高Java技能)編譯:ImportNew/唐尤華dzone.com/articles/java-simpledateformat-is-not-simple
  • 還在使用SimpleDateFormat?
    來看看SimpleDateFormat的format()方法的源碼:注意, calendar.setTime(date),SimpleDateFormat的format方法實際操作的就是Calendar。
  • 還在使用 SimpleDateFormat?你的項目崩沒?
    sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");    public static String formatDate(Date date) throws ParseException {        return sdf.format(date);    }    public static Date
  • 2020 年,你還在使用 Java 中的 SimpleDateFormat 嗎?
    這個時候我們就會使用到SimpleDateFormat 類,比如使用下面的代碼來獲取當前時間,並調用SimpleDateFormat 對時間進行格式化:  Date date = new Date();  SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//設置日期格式  String
  • 還在使用SimpleDateFormat?你的項目崩沒?
    :public class SimpleDateFormatTest {    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");    public static String formatDate(Date date)
  • SimpleDateFormat 如何安全的使用?
    線程不安全在 SimpleDateFormat 類的 JavaDoc 中,描述了該類不能夠保證線程安全,建議為每個線程創建單獨的日期/時間格式實例,如果多個線程同時訪問一個日期/時間格式,它必須在外部進行同步。那麼在多線程環境下調用 format() 和 parse() 方法應該使用同步代碼來避免問題。
  • 筆記總結Date、Calendar、SimpleDateFormat和DecimalFormat
    (即年月日時分秒) public classTest01 {      public static void main(String[] args) {//獲取當前系統時間日期對象Date date1 = new Date();System.out.println("當前時間
  • 不簡單的 SimpleDateFormat
    在 Java 中格式化和解析日期的一種常見方法是使用 SimpleDateFormat。下面是我們用到的一個公共類。e) { e.printStackTrace(); } return null; } public static String format(Date target) { return SIMPLE_DATE_FORMAT.format(target); }}你覺得它會像我們預期的那樣進行工作麼
  • 還在使用SimpleDateFormat?
    來看看SimpleDateFormat的format()方法的源碼:注意, calendar.setTime(date),SimpleDateFormat的format方法實際操作的就是Calendar。
  • 不簡單的 Java SimpleDateFormat
    在 Java 中格式化和解析日期的一種常見方法是使用 SimpleDateFormat。下面是我們用到的一個公共類。e) { e.printStackTrace(); } return null; } public static String format(Date target) { return SIMPLE_DATE_FORMAT.format(target); }}你覺得它會像我們預期的那樣進行工作麼
  • 【107期】SimpleDateFormat 的線程安全問題與解決方案
    原因 SimpleDateFormat(下面簡稱sdf)類內部有一個Calendar對象引用,它用來儲存和這個sdf相關的日期信息,例如sdf.parse(dateStr), sdf.format(date) 諸如此類的方法參數傳入的日期相關String, Date等等, 都是交友Calendar引用來儲存的.
  • 你知道 SimpleDateFormat 的性能問題怎麼解決嗎?
    推薦做法下面是推薦你在項目中實現的工具類寫法,可以一定程度提升性能。public final class SimpleDateFormatUtils {    public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";    /**     * 線程安全轉換 String to Date     *
  • 阿里為什麼建議使用LocalDateTime ,而不是 Date?
    在項目開發過程中經常遇到時間處理,但是你真的用對了嗎,理解阿里巴巴開發手冊中禁用static修飾SimpleDateFormat嗎通過閱讀本篇文章你將了解到:為什麼需要LocalDate、LocalTime、LocalDateTime【java8新提供的類】java8新的時間API的使用方式,包括創建
  • 面試官一步一步的套路你,為什麼SimpleDateFormat不是線程安全的
    小小白:噼裡啪啦敲完了,代碼如下:public class SimpleDateFormatTest { static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() { @Override
  • 使用 LocalDateTime 而不是 Date
    作者 | 何甜甜在嗎來源 | juejin.im/post/5d7787625188252388753eae在項目開發過程中經常遇到時間處理,但是你真的用對了嗎,理解阿里巴巴開發手冊中禁用static修飾
  • Static 關鍵字的 5 種用法,你會幾種?
    volatile SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");        public static  String formatFromDate(Date date)throws ParseException{        return sdf.format(date);
  • 為什麼建議你使用 LocalDateTime ,而不是 Date?
    ,但是你真的用對了嗎,理解阿里巴巴開發手冊中禁用static修飾SimpleDateFormat嗎通過閱讀本篇文章你將了解到:為什麼需要LocalDate、LocalTime、LocalDateTimeDate如果不格式化,列印出的日期可讀性差Tue Sep 10 09:34:04 CST 2019使用SimpleDateFormat
  • 你還在用 Date?快使用 LocalDateTime 了!
    本文經由掘金作者:何甜甜在嗎 授權轉載原文連結:https://juejin.im/post/5d7787625188252388753eae在項目開發過程中經常遇到時間處理,但是你真的用對了嗎,理解阿里巴巴開發手冊中禁用static修飾SimpleDateFormat嗎?
  • 為什麼建議使用你 LocalDateTime ,而不是 Date?
    (給ImportNew加星標,提高Java技能)轉自:何甜甜在嗎連結:http://juejin.im/post/5d7787625188252388753eae在項目開發過程中經常遇到時間處理,但是你真的用對了嗎,理解阿里巴巴開發手冊中禁用static修飾SimpleDateFormat嗎