国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Java之SimpleDateFormat的用法詳解

這篇具有很好參考價值的文章主要介紹了Java之SimpleDateFormat的用法詳解。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

在日常開發(fā)中,我們經(jīng)常會用到時間,我們有很多辦法在Java代碼中獲取時間。但是不同的方法獲取到的時間的格式都不盡相同,這時候就需要一種格式化工具,把時間顯示成我們需要的格式。

最常用的方法就是使用SimpleDateFormat類。這是一個看上去功能比較簡單的類,但是,一旦使用不當(dāng)也有可能導(dǎo)致很大的問題。

在阿里巴巴Java開發(fā)手冊中,有如下明確規(guī)定:

那么,本文就圍繞SimpleDateFormat的用法、原理等來深入分析下如何以正確的姿勢使用它。

SimpleDateFormat用法

SimpleDateFormat是Java提供的一個格式化和解析日期的工具類。它允許進(jìn)行格式化(日期 -> 文本)、解析(文本 -> 日期)和規(guī)范化。SimpleDateFormat 使得可以選擇任何用戶定義的日期-時間格式的模式。

在Java中,可以使用SimpleDateFormat的format方法,將一個Date類型轉(zhuǎn)化成String類型,并且可以指定輸出格式。

// Date轉(zhuǎn)String
Date data = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dataStr = sdf.format(data);
System.out.println(dataStr);

以上代碼,轉(zhuǎn)換的結(jié)果是:2018-11-25 13:00:00,日期和時間格式由"日期和時間模式"字符串指定。如果你想要轉(zhuǎn)換成其他格式,只要指定不同的時間模式就行了。

在Java中,可以使用SimpleDateFormat的parse方法,將一個String類型轉(zhuǎn)化成Date類型。

// String轉(zhuǎn)Data
System.out.println(sdf.parse(dataStr));
日期和時間模式表達(dá)方法

在使用SimpleDateFormat的時候,需要通過字母來描述時間元素,并組裝成想要的日期和時間模式。常用的時間元素和字母的對應(yīng)表如下:

![-w717][1]?

模式字母通常是重復(fù)的,其數(shù)量確定其精確表示。如下表是常用的輸出格式的表示方法。

![-w535][2]?

輸出不同時區(qū)的時間

時區(qū)是地球上的區(qū)域使用同一個時間定義。以前,人們通過觀察太陽的位置(時角)決定時間,這就使得不同經(jīng)度的地方的時間有所不同(地方時)。1863年,首次使用時區(qū)的概念。時區(qū)通過設(shè)立一個區(qū)域的標(biāo)準(zhǔn)時間部分地解決了這個問題。

世界各個國家位于地球不同位置上,因此不同國家,特別是東西跨度大的國家日出、日落時間必定有所偏差。這些偏差就是所謂的時差。

現(xiàn)今全球共分為24個時區(qū)。由于實用上常常1個國家,或1個省份同時跨著2個或更多時區(qū),為了照顧到行政上的方便,常將1個國家或1個省份劃在一起。所以時區(qū)并不嚴(yán)格按南北直線來劃分,而是按自然條件來劃分。例如,中國幅員寬廣,差不多跨5個時區(qū),但為了使用方便簡單,實際上在只用東八時區(qū)的標(biāo)準(zhǔn)時即北京時間為準(zhǔn)。

由于不同的時區(qū)的時間是不一樣的,甚至同一個國家的不同城市時間都可能不一樣,所以,在Java中想要獲取時間的時候,要重點關(guān)注一下時區(qū)問題。

默認(rèn)情況下,如果不指明,在創(chuàng)建日期的時候,會使用當(dāng)前計算機所在的時區(qū)作為默認(rèn)時區(qū),這也是為什么我們通過只要使用new Date()就可以獲取中國的當(dāng)前時間的原因。

那么,如何在Java代碼中獲取不同時區(qū)的時間呢?SimpleDateFormat可以實現(xiàn)這個功能。

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
System.out.println(sdf.format(Calendar.getInstance().getTime()));

以上代碼,轉(zhuǎn)換的結(jié)果是: 2018-11-24 21:00:00 。既中國的時間是11月25日的13點,而美國洛杉磯時間比中國北京時間慢了16個小時(這還和冬夏令時有關(guān)系,就不詳細(xì)展開了)。

如果你感興趣,你還可以嘗試打印一下美國紐約時間(America/New_York)。紐約時間是2018-11-25 00:00:00。紐約時間比中國北京時間早了13個小時。

當(dāng)然,這不是顯示其他時區(qū)的唯一方法,不過本文主要為了介紹SimpleDateFormat,其他方法暫不介紹了。

SimpleDateFormat線程安全性

由于SimpleDateFormat比較常用,而且在一般情況下,一個應(yīng)用中的時間顯示模式都是一樣的,所以很多人愿意使用如下方式定義SimpleDateFormat:

public class Main {

    private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static void main(String[] args) {
        simpleDateFormat.setTimeZone(TimeZone.getTimeZone("America/New_York"));
        System.out.println(simpleDateFormat.format(Calendar.getInstance().getTime()));
    }
}

這種定義方式,存在很大的安全隱患。

問題重現(xiàn)

我們來看一段代碼,以下代碼使用線程池來執(zhí)行時間輸出。

   /** * @author Hollis */ 
   public class Main {

    /**
     * 定義一個全局的SimpleDateFormat
     */
    private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    /**
     * 使用ThreadFactoryBuilder定義一個線程池
     */
    private static ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
        .setNameFormat("demo-pool-%d").build();

    private static ExecutorService pool = new ThreadPoolExecutor(5, 200,
        0L, TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

    /**
     * 定義一個CountDownLatch,保證所有子線程執(zhí)行完之后主線程再執(zhí)行
     */
    private static CountDownLatch countDownLatch = new CountDownLatch(100);

    public static void main(String[] args) {
        //定義一個線程安全的HashSet
        Set<String> dates = Collections.synchronizedSet(new HashSet<String>());
        for (int i = 0; i < 100; i++) {
            //獲取當(dāng)前時間
            Calendar calendar = Calendar.getInstance();
            int finalI = i;
            pool.execute(() -> {
                    //時間增加
                    calendar.add(Calendar.DATE, finalI);
                    //通過simpleDateFormat把時間轉(zhuǎn)換成字符串
                    String dateString = simpleDateFormat.format(calendar.getTime());
                    //把字符串放入Set中
                    dates.add(dateString);
                    //countDown
                    countDownLatch.countDown();
            });
        }
        //阻塞,直到countDown數(shù)量為0
        countDownLatch.await();
        //輸出去重后的時間個數(shù)
        System.out.println(dates.size());
    }
}

以上代碼,其實比較簡單,很容易理解。就是循環(huán)一百次,每次循環(huán)的時候都在當(dāng)前時間基礎(chǔ)上增加一個天數(shù)(這個天數(shù)隨著循環(huán)次數(shù)而變化),然后把所有日期放入一個線程安全的、帶有去重功能的Set中,然后輸出Set中元素個數(shù)。

上面的例子我特意寫的稍微復(fù)雜了一些,不過我?guī)缀醵技恿俗⑨?。這里面涉及到了[線程池的創(chuàng)建][3]、[CountDownLatch][4]、lambda表達(dá)式、線程安全的HashSet等知識。感興趣的朋友可以逐一了解一下。

正常情況下,以上代碼輸出結(jié)果應(yīng)該是100。但是實際執(zhí)行結(jié)果是一個小于100的數(shù)字。

原因就是因為SimpleDateFormat作為一個非線程安全的類,被當(dāng)做了共享變量在多個線程中進(jìn)行使用,這就出現(xiàn)了線程安全問題。

在阿里巴巴Java開發(fā)手冊的第一章第六節(jié)——并發(fā)處理中關(guān)于這一點也有明確說明:

那么,接下來我們就來看下到底是為什么,以及該如何解決。

線程不安全原因

通過以上代碼,我們發(fā)現(xiàn)了在并發(fā)場景中使用SimpleDateFormat會有線程安全問題。其實,JDK文檔中已經(jīng)明確表明了SimpleDateFormat不應(yīng)該用在多線程場景中:

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.

那么接下來分析下為什么會出現(xiàn)這種問題,SimpleDateFormat底層到底是怎么實現(xiàn)的?

我們跟一下SimpleDateFormat類中format方法的實現(xiàn)其實就能發(fā)現(xiàn)端倪。

![][5]?

SimpleDateFormat中的format方法在執(zhí)行過程中,會使用一個成員變量calendar來保存時間。這其實就是問題的關(guān)鍵。

由于我們在聲明SimpleDateFormat的時候,使用的是static定義的。那么這個SimpleDateFormat就是一個共享變量,隨之,SimpleDateFormat中的calendar也就可以被多個線程訪問到。

假設(shè)線程1剛剛執(zhí)行完calendar.setTime把時間設(shè)置成2018-11-11,還沒等執(zhí)行完,線程2又執(zhí)行了calendar.setTime把時間改成了2018-12-12。這時候線程1繼續(xù)往下執(zhí)行,拿到的calendar.getTime得到的時間就是線程2改過之后的。

除了format方法以外,SimpleDateFormat的parse方法也有同樣的問題。

所以,不要把SimpleDateFormat作為一個共享變量使用。

如何解決

前面介紹過了SimpleDateFormat存在的問題以及問題存在的原因,那么有什么辦法解決這種問題呢?

解決方法有很多,這里介紹三個比較常用的方法。

使用局部變量

for (int i = 0; i < 100; i++) {
    //獲取當(dāng)前時間
    Calendar calendar = Calendar.getInstance();
    int finalI = i;
    pool.execute(() -> {
        // SimpleDateFormat聲明成局部變量
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //時間增加
        calendar.add(Calendar.DATE, finalI);
        //通過simpleDateFormat把時間轉(zhuǎn)換成字符串
        String dateString = simpleDateFormat.format(calendar.getTime());
        //把字符串放入Set中
        dates.add(dateString);
        //countDown
        countDownLatch.countDown();
    });
}

SimpleDateFormat變成了局部變量,就不會被多個線程同時訪問到了,就避免了線程安全問題。

加同步鎖

除了改成局部變量以外,還有一種方法大家可能比較熟悉的,就是對于共享變量進(jìn)行加鎖。

for (int i = 0; i < 100; i++) {
    //獲取當(dāng)前時間
    Calendar calendar = Calendar.getInstance();
    int finalI = i;
    pool.execute(() -> {
        //加鎖
        synchronized (simpleDateFormat) {
            //時間增加
            calendar.add(Calendar.DATE, finalI);
            //通過simpleDateFormat把時間轉(zhuǎn)換成字符串
            String dateString = simpleDateFormat.format(calendar.getTime());
            //把字符串放入Set中
            dates.add(dateString);
            //countDown
            countDownLatch.countDown();
        }
    });
}

通過加鎖,使多個線程排隊順序執(zhí)行。避免了并發(fā)導(dǎo)致的線程安全問題。

其實以上代碼還有可以改進(jìn)的地方,就是可以把鎖的粒度再設(shè)置的小一點,可以只對simpleDateFormat.format這一行加鎖,這樣效率更高一些。

使用ThreadLocal

第三種方式,就是使用 ThreadLocal。 ThreadLocal 可以確保每個線程都可以得到單獨的一個 SimpleDateFormat 的對象,那么自然也就不存在競爭問題了。

/**
 * 使用ThreadLocal定義一個全局的SimpleDateFormat
 */
private static ThreadLocal<SimpleDateFormat> simpleDateFormatThreadLocal = new ThreadLocal<SimpleDateFormat>() {
    @Override
    protected SimpleDateFormat initialValue() {
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    }
};

//用法
String dateString = simpleDateFormatThreadLocal.get().format(calendar.getTime());

用 ThreadLocal 來實現(xiàn)其實是有點類似于緩存的思路,每個線程都有一個獨享的對象,避免了頻繁創(chuàng)建對象,也避免了多線程的競爭。

當(dāng)然,以上代碼也有改進(jìn)空間,就是,其實SimpleDateFormat的創(chuàng)建過程可以改為延遲加載。這里就不詳細(xì)介紹了。

使用DateTimeFormatter

如果是Java8應(yīng)用,可以使用DateTimeFormatter代替SimpleDateFormat,這是一個線程安全的格式化工具類。就像官方文檔中說的,這個類 simple beautiful strong immutable thread-safe。

//解析日期
String dateStr= "2016年10月25日";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
LocalDate date= LocalDate.parse(dateStr, formatter);

//日期轉(zhuǎn)換為字符串
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy年MM月dd日 hh:mm a");
String nowStr = now .format(format);
System.out.println(nowStr);

總結(jié)

本文介紹了SimpleDateFormat的用法,SimpleDateFormat主要可以在String和Date之間做轉(zhuǎn)換,還可以將時間轉(zhuǎn)換成不同時區(qū)輸出。同時提到在并發(fā)場景中SimpleDateFormat是不能保證線程安全的,需要開發(fā)者自己來保證其安全性。

主要的幾個手段有改為局部變量、使用synchronized加鎖、使用Threadlocal為每一個線程單獨創(chuàng)建一個等。

希望通過此文,你可以在使用SimpleDateFormat的時候更加得心應(yīng)手。文章來源地址http://www.zghlxwxcb.cn/news/detail-849444.html

到了這里,關(guān)于Java之SimpleDateFormat的用法詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • Java SimpleDateFormat設(shè)置時區(qū)導(dǎo)致時間不正確的解決方案

    問題介紹 在Android開發(fā)中經(jīng)常遇到有的接口需要上傳當(dāng)前時間,如果后臺要求直接傳一個long類型的時間戳還好,因為這個時間戳是跟時區(qū)無關(guān)的,如果后臺接口要求傳的是格式化的時間,若本地設(shè)備設(shè)置的時區(qū)與后臺要就的時區(qū)不一致,就會導(dǎo)致上傳的時間不準(zhǔn)確。 不完善的

    2024年02月11日
    瀏覽(17)
  • Java程序設(shè)計入門教程--日期格式化類SimpleDateFormat

    Java程序設(shè)計入門教程--日期格式化類SimpleDateFormat

    ? ? ? ?在程序設(shè)計中,經(jīng)常用到特定的日期格式,此時就可以使用 java.text 包中的 SimpleDateFormat 類來對日期時間進(jìn)行格式化,如可以將日期轉(zhuǎn)換為指定格式的文本,也可將文本轉(zhuǎn)換為日期。 目標(biāo)格式 使用SimpleDateFormat類時,首先要定義一個要轉(zhuǎn)換的日期時間目標(biāo)格式。目標(biāo)格

    2024年02月07日
    瀏覽(19)
  • Java面試題:SimpleDateFormat是線程安全的嗎?使用時應(yīng)該注意什么?

    在Java開發(fā)中,我們經(jīng)常需要獲取和處理時間,這需要使用到各種不同的方法。其中,使用SimpleDateFormat類來格式化時間是一種常見的方法。雖然這個類看上去功能比較簡單,但是如果使用不當(dāng),也可能會引發(fā)一些問題。 首先我們要明確一點, SimpleDateFormat不是線程安全的 。

    2024年04月26日
    瀏覽(41)
  • 想讓你的代碼簡潔,試試這個SimpleDateFormat類高深用法

    想讓你的代碼簡潔,試試這個SimpleDateFormat類高深用法

    本文分享自華為云社區(qū)《從入門到精通:SimpleDateFormat類高深用法,讓你的代碼更簡潔!》,作者:bug菌。 @[toc] 日期時間在開發(fā)中是非常常見的需求,尤其是在處理與時間相關(guān)的業(yè)務(wù)邏輯時,我們需要對日期時間進(jìn)行格式化、比較等操作。在Java中,我們可以使用 SimpleDateFor

    2024年02月08日
    瀏覽(25)
  • Java中日期時間格式化方法SimpleDateFormat和DateTimeFormatter使用完整示例及區(qū)別說明

    Java中日期時間格式化方法SimpleDateFormat和DateTimeFormatter使用完整示例及區(qū)別說明

    示例代碼: 示例截圖: ?這里完整的用兩種方法分別實現(xiàn)了日期和String的來回轉(zhuǎn)換,鑒于SimpleDateFormat早已過時,且非線程安全,所以推薦大家首選使用DateTimeFormatter,用法基本都是差不多的。變化不大。但是DateTimeFormatter需要Java Level 8(8 - Lambdas, type annotations etc.),需留意。

    2023年04月09日
    瀏覽(26)
  • 【JAVA語言-第12話】API中的工具類 之 Date,DateFormat,SimpleDateFormat,Calendar類的詳細(xì)解析

    【JAVA語言-第12話】API中的工具類 之 Date,DateFormat,SimpleDateFormat,Calendar類的詳細(xì)解析

    目錄 日期和時間 1.1 Date類 1.1.1 概述 1.1.2 常用方法? 1.1.3 案例 1.2 DateFormat類? 1.2.1 概述 1.2.2 常用方法 1.3 SimpleDateFormat類 1.3.1 概述 1.3.2 構(gòu)造方法 1.3.3 模式字符? 1.3.4 日期轉(zhuǎn)字符串 1.3.5 字符串轉(zhuǎn)日期 1.4?Calendar類 1.4.1 概述 1.4.2 靜態(tài)方法 1.4.3 常用成員方法 1.4.4 成員方法中參數(shù)

    2024年02月02日
    瀏覽(21)
  • SimpleDateFormat 線程安全問題修復(fù)方案

    在日常的開發(fā)過程中,我們不可避免地會使用到 JDK8 之前的 Date 類,在格式化日期或解析日期時就需要用到 SimpleDateFormat 類,但由于該類并不是線程安全的,所以我們常發(fā)現(xiàn)對該類的不恰當(dāng)使用會導(dǎo)致日期解析異常,從而影響線上服務(wù)可用率。 以下是對 SimpleDateFormat 類不恰當(dāng)

    2024年02月12日
    瀏覽(23)
  • 日期與時間【Date/SimpleDateFormat/Calendar】

    視頻鏈接:https://www.bilibili.com/video/BV1Cv411372m?p=121vd_source=9140dcc493e34a9f4e95ca2f8f71bbd3 Date類的對象在java中代表的是當(dāng)前所在系統(tǒng)的此刻日期時間。 Date的構(gòu)造器 public Date():創(chuàng)建一個Date對象,代表的是系統(tǒng)當(dāng)前此刻日期時間。 Date的常用方法 public long getTime():獲取時間對象的毫秒值

    2024年02月03日
    瀏覽(28)
  • SimpleDateFormat為什么是線程不安全的?

    SimpleDateFormat為什么是線程不安全的?

    大家好,我是哪吒。 在日常開發(fā)中,Date工具類使用頻率相對較高,大家通常都會這樣寫: 這很簡單啊,有什么爭議嗎? 你應(yīng)該聽過“時區(qū)”這個名詞,大家也都知道,相同時刻不同時區(qū)的時間是不一樣的。 因此在使用時間時,一定要給出時區(qū)信息。 對于當(dāng)前的上海時區(qū)和

    2024年02月20日
    瀏覽(23)
  • SimpleDateFormat :{ ParseException: Unparseable date} 問題原因以及解決方法

    SimpleDateFormat simpleFormat = new SimpleDateFormat(“yyyy-MM-dd hh:mm:ss”); 我所使用的與實際要轉(zhuǎn)換的不一致,導(dǎo)致報錯 在轉(zhuǎn)換的時候必須保持 轉(zhuǎn)換字符串和轉(zhuǎn)換類型格式一致 提供一個代碼片段(只是簡單做了一下判斷,只能滿足幾種日期轉(zhuǎn)換( 寫的不是很好,有待優(yōu)化 )) 參考此篇

    2024年02月14日
    瀏覽(20)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包