電腦程式的思維邏輯 (32) - 剖析日期和時間

来源:http://www.cnblogs.com/swiftma/archive/2016/08/16/5774483.html
-Advertisement-
Play Games

本節剖析Java API中的日期和時間相關類,電腦內部是如何表示時間的?Date類的含義是什麼?Calendar完成了什麼功能,能進行哪些操作?內部是如何實現的?體現了怎樣的設計模式?Date與字元串如何相互轉換?這些類有什麼不足?... ...


本節和下節,我們討論在Java中如何進行日期和時間相關的操作。

日期和時間是一個比較複雜的概念,Java API中對它的支持不是特別好,有一個第三方的類庫反而特別受歡迎,這個類庫是Joda-Time,Java 1.8受Joda-Time影響,重新設計了日期和時間API,新增了一個包java.time。

雖然之前的設計有一些不足,但Java API依然是被大量使用的,本節介紹Java 1.8之前API中對日期和時間的支持,下節介紹Joda-Time,Java 1.8中的新API與Joda-Time比較類似,暫時就不介紹了。

關於日期和時間,有一些基本概念,我們先來看下。

基本概念

時區

我們都知道,同一時刻,世界上各個地區的時間可能是不一樣的,具體時間與時區有關,一共有24個時區,英國格林尼治是0時區,北京是東八區,也就是說格林尼治凌晨1點,北京是早上9點。0時區的時間也稱為GMT+0時間,GMT是格林尼治標準時間,北京的時間就是GMT+8:00。

時刻和Epoch Time (紀元時)

所有電腦系統內部都用一個整數表示時刻,這個整數是距離格林尼治標準時間1970年1月1日0時0分0秒的毫秒數。為什麼要用這個時間呢?更多的是歷史原因,本文就不介紹了。

格林尼治標準時間1970年1月1日0時0分0秒也被稱為Epoch Time (紀元時)。

這個整數表示的是一個時刻,與時區無關,世界上各個地方都是同一個時刻,但各個地區對這個時刻的解讀,如年月日時分秒,可能是不一樣的。

如何表示1970年以前的時間呢?使用負數。

年曆

我們都知道,中國有西曆和農曆之分,西曆和農曆都是年曆,不同的年曆,一年有多少月,每月有多少天,甚至一天有多少小時,這些可能都是不一樣的。

比如,西曆有閏年,閏年2月是29天,而其他年份則是28天,其他月份,有的是30天,有的是31天。農曆有閏月,比如閏7月,一年就會有兩個7月,一共13個月。

西曆是世界上廣泛採用的年曆,除了西曆,還有其他一些年曆,比如日本也有自己的年曆。Java API的設計思想是支持國際化的,支持多種年曆,但實際中沒有直接支持中國的農曆,本文主要討論西曆。

簡單總結下,時刻是一個絕對時間,對時刻的解讀,如年月日周時分秒等,則是相對的,與年曆和時區相關。

Java日期和時間API

Java API中關於日期和時間,有三個主要的類:

  • Date:表示時刻,即絕對時間,與年月日無關。
  • Calendar:表示年曆,Calendar是一個抽象類,其中表示西曆的子類是GregorianCalendar
  • DateFormat:表示格式化,能夠將日期和時間與字元串進行相互轉換,DateFormat也是一個抽象類,其中最常用的子類是SimpleDateFormat。 

還有兩個相關的類:

  • TimeZone: 表示時區
  • Locale: 表示國家和語言 

下麵,我們來看這些類。

Date

Date是Java API中最早引入的關於日期的類,一開始,Date也承載了關於年曆的角色,但由於不能支持國際化,其中的很多方法都已經過時了,被標記為了@Deprecated,不再建議使用。

Date表示時刻,內部主要是一個long類型的值,如下所示:

private transient long fastTime;

fastTime表示距離紀元時的毫秒數,此處,關於transient關鍵字,我們暫時忽略。

Date有兩個構造方法:

public Date(long date) {
    fastTime = date;
}

public Date() {
    this(System.currentTimeMillis());
}

第一個構造方法,就是根據傳入的毫秒數進行初始化,第二個構造方法是預設構造方法,它根據System.currentTimeMillis()的返回值進行初始化。System.currentTimeMillis()是一個常用的方法,它返回當前時刻距離紀元時的毫秒數。

Date中的大部分方法都已經過時了,其中沒有過時的主要方法有:

返回毫秒數

public long getTime() 

判斷與其他Date是否相同

public boolean equals(Object obj)

主要就是比較內部的毫秒數是否相同。

與其他Date進行比較

public int compareTo(Date anotherDate)

Date實現了Comparable介面,比較也是比較內部的毫秒數,如果當前Date的毫秒數小於參數中的,返回-1,相同返回0,否則返回1。

除了compareTo,還有另外兩個方法,與給定日期比較,判斷是否在給定日期之前或之後,內部比較的也是毫秒數。

public boolean before(Date when)
public boolean after(Date when)

哈希值

public int hashCode()

哈希值演算法與Long類似。

TimeZone

TimeZone表示時區,它是一個抽象類,有靜態方法用於獲取其實例。

獲取當前的預設時區,代碼為:

TimeZone tz = TimeZone.getDefault();
System.out.println(tz.getID());

獲取預設時區,並輸出其ID,在我的電腦上,輸出為:

Asia/Shanghai

預設時區是在哪裡設置的呢,可以更改嗎?Java中有一個系統屬性,user.timezone,保存的就是預設時區,系統屬性可以通過System.getProperty獲得,如下所示:

System.out.println(System.getProperty("user.timezone"));

在我的電腦上,輸出為:

Asia/Shanghai

系統屬性可以在Java啟動的時候傳入參數進行更改,如

java -Duser.timezone=Asia/Shanghai xxxx

TimeZone也有靜態方法,可以獲得任意給定時區的實例,比如:

獲取美國東部時區

TimeZone tz = TimeZone.getTimeZone("US/Eastern");

ID除了可以是名稱外,還可以是GMT形式表示的時區,如:

TimeZone tz = TimeZone.getTimeZone("GMT+08:00");

國家和語言Locale

Locale表示國家和語言,它有兩個主要參數,一個是國家,另一個是語言,每個參數都有一個代碼,不過國家並不是必須的。

比如說,中國的大陸代碼是CN,臺灣地區的代碼是TW,美國的代碼是US,中文語言的代碼是zh,英文是en。

Locale類中定義了一些靜態變數,表示常見的Locale,比如:

  • Locale.US:表示美國英語
  • Locale.ENGLISH:表示所有英語
  • Locale.TAIWAN:表示臺灣中文
  • Locale.CHINESE:表示所有中文
  • Locale.SIMPLIFIED_CHINESE:表示大陸中文

與TimeZone類似,Locale也有靜態方法獲取預設值,如:

Locale locale = Locale.getDefault();
System.out.println(locale.toString());

在我的電腦上,輸出為:

zh_CN

Calendar

Calendar類是日期和時間操作中的主要類,它表示與TimeZone和Locale相關的日曆信息,可以進行各種相關的運算。

我們先來看下它的內部組成。

內部組成

與Date類似,Calendar內部也有一個表示時刻的毫秒數,定義為:

protected long  time;

除此之外,Calendar內部還有一個數組,表示日曆中各個欄位的值,定義為:

protected int   fields[];

這個數組的長度為17,保存一個日期中各個欄位的值,都有哪些欄位呢?Calendar類中定義了一些靜態變數,表示這些欄位,主要有:

  • Calendar.YEAR:表示年
  • Calendar.MONTH:表示月,一月份是0,Calendar同樣定義了表示各個月份的靜態變數,如Calendar.JULY表示7月。
  • Calendar.DAY_OF_MONTH:表示日,每月的第一天是1。
  • Calendar.HOUR_OF_DAY:表示小時,從0到23。
  • Calendar.MINUTE:表示分鐘,0到59。
  • Calendar.SECOND:表示秒,0到59。
  • Calendar.MILLISECOND:表示毫秒,0到999。
  • Calendar.DAY_OF_WEEK:表示星期幾,周日是1,周一是2,周六是7,Calenar同樣定義了表示各個星期的靜態變數,如Calendar.SUNDAY表示周日。 

獲取Calendar實例

Calendar是抽象類,不能直接創建對象,它提供了四個靜態方法,可以獲取Calendar實例,分別為:

public static Calendar getInstance()
public static Calendar getInstance(Locale aLocale)
public static Calendar getInstance(TimeZone zone)
public static Calendar getInstance(TimeZone zone, Locale aLocale)

最終調用的方法都是需要TimeZone和Locale的,如果沒有,則會使用上面介紹的預設值。getInstance方法會根據TimeZone和Locale創建對應的Calendar子類對象,在中文系統中,子類一般是表示西曆的GregorianCalendar。

getInstance方法封裝了Calendar對象創建的細節,TimeZone和Locale不同,具體的子類可能不同,但都是Calendar,這種隱藏對象創建細節的方式,是電腦程式中一種常見的設計模式,它有一個名字,叫工廠方法,getInstance就是一個工廠方法,它生產對象。

獲取日曆信息

與new Date()類似,新創建的Calendar對象表示的也是當前時間,與Date不同的是,Calendar對象可以方便的獲取年月日等日曆信息。

來看代碼,輸出當前時間的各種信息:

Calendar calendar = Calendar.getInstance();
System.out.println("year: "+calendar.get(Calendar.YEAR));
System.out.println("month: "+calendar.get(Calendar.MONTH));
System.out.println("day: "+calendar.get(Calendar.DAY_OF_MONTH));
System.out.println("hour: "+calendar.get(Calendar.HOUR_OF_DAY));
System.out.println("minute: "+calendar.get(Calendar.MINUTE));
System.out.println("second: "+calendar.get(Calendar.SECOND));
System.out.println("millisecond: " +calendar.get(Calendar.MILLISECOND));
System.out.println("day_of_week: " + calendar.get(Calendar.DAY_OF_WEEK));

具體輸出與執行時的時間和預設的TimeZone以及Locale有關,在寫作時,我的電腦上的輸出為:

year: 2016
month: 7
day: 14
hour: 13
minute: 55
second: 51
millisecond: 564
day_of_week: 2

內部,Calendar會將表示時刻的毫秒數,按照TimeZone和Locale對應的年曆,計算各個日曆欄位的值,存放在fields數組中,Calendar.get方法獲取的就是fields數組中對應欄位的值。

設置和修改時間

Calendar支持根據Date或毫秒數設置時間:

public final void setTime(Date date)
public void setTimeInMillis(long millis)

也支持根據年月日等日曆欄位設置時間:

public final void set(int year, int month, int date)
public final void set(int year, int month, int date, int hourOfDay, int minute)
public final void set(int year, int month, int date, int hourOfDay, int minute, int second)
public void set(int field, int value)

除了直接設置,Calendar支持根據欄位增加和減少時間:

public void add(int field, int amount)

amount為正數表示增加,負數表示減少。

比如說,如果想設置Calendar為第二天的下午2點15,代碼可以為:

Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_MONTH, 1);
calendar.set(Calendar.HOUR_OF_DAY, 14);
calendar.set(Calendar.MINUTE, 15);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);

Calendar的這些方法中一個比較方便和強大的地方在於,它能夠自動調整相關的欄位。

比如說,我們知道二月份最多有29天,如果當前時間為1月30號,對Calendar.MONTH欄位加1,即增加一月,Calendar不是簡單的只對月欄位加1,那樣日期是2月30號,是無效的,Calendar會自動調整為2月最後一天,即2月28或29。

再比如,設置的值可以超出其欄位最大範圍,Calendar會自動更新其他欄位,如:

Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.HOUR_OF_DAY, 48);
calendar.add(Calendar.MINUTE, -120);

相當於增加了46小時。

內部,根據欄位設置或修改時間時,Calendar會更新fields數組對應欄位的值,但一般不會立即更新其他相關欄位或內部的毫秒數的值,不過在獲取時間或欄位值的時候,Calendar會重新計算並更新相關欄位。

簡單總結下,Calenar做了一項非常繁瑣的工作,根據TimeZone和Locale,在絕對時間毫秒數和日曆欄位之間自動進行轉換,且對不同日曆欄位的修改進行自動同步更新。

除了add,Calendar還有一個類似的方法:

public void roll(int field, int amount)

與add的區別是,這個方法不影響時間範圍更大的欄位值。比如說:

Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 13);
calendar.set(Calendar.MINUTE, 59);
calendar.add(Calendar.MINUTE, 3);

calendar首先設置為13:59,然後分鐘欄位加3,執行後的calendar時間為14:02。如果add改為roll,即:

calendar.roll(Calendar.MINUTE, 3);

則執行後的calendar時間會變為13:02,在分鐘欄位上執行roll不會改變小時的值。

轉換為Date或毫秒數

Calendar可以方便的轉換為Date或毫秒數,方法是:

public final Date getTime()
public long getTimeInMillis() 

Calendar的比較

與Date類似,Calendar之間也可以進行比較,也實現了Comparable介面,相關方法有:

public boolean equals(Object obj)
public int compareTo(Calendar anotherCalendar)
public boolean after(Object when)
public boolean before(Object when)

DateFormat

DateFormat類主要在Date和字元串表示之間進行相互轉換,它有兩個主要的方法:

public final String format(Date date)
public Date parse(String source)

format將Date轉換為字元串,parse將字元串轉換為Date。

Date的字元串表示與TimeZone和Locale都是相關的,除此之外,還與兩個格式化風格有關,一個是日期的格式化風格,另一個是時間的格式化風格。

DateFormat定義了四個靜態變數,表示四種風格,SHORT、MEDIUM、LONG和FULL,還定義了一個靜態變數DEFAULT,表示預設風格,值為MEDIUM,不同風格輸出的信息詳細程度不同。

與Calendar類似,DateFormat也是抽象類,也用工廠模式創建對象,提供了多個靜態方法創建DateFormat對象,有三類方法:

public final static DateFormat getDateTimeInstance()
public final static DateFormat getDateInstance()
public final static DateFormat getTimeInstance()

getDateTimeInstance既處理日期也處理時間,getDateInstance只處理日期,getTimeInstance只處理時間,看下麵代碼:

Calendar calendar = Calendar.getInstance();
//2016-08-15 14:15:20
calendar.set(2016, 07, 15, 14, 15, 20);
System.out.println(DateFormat.getDateTimeInstance()
        .format(calendar.getTime()));
System.out.println(DateFormat.getDateInstance()
        .format(calendar.getTime()));
System.out.println(DateFormat.getTimeInstance()
        .format(calendar.getTime()));

輸出為:

2016-8-15 14:15:20
2016-8-15
14:15:20

每類工廠方法都有兩個重載的方法,接受日期和時間風格以及Locale作為參數:

DateFormat getDateTimeInstance(int dateStyle, int timeStyle)
DateFormat getDateTimeInstance(int dateStyle, int timeStyle, Locale aLocale)

比如,看下麵代碼:

Calendar calendar = Calendar.getInstance();
//2016-08-15 14:15:20
calendar.set(2016, 07, 15, 14, 15, 20);
System.out.println(DateFormat.getDateTimeInstance(
        DateFormat.LONG,DateFormat.SHORT,Locale.CHINESE)
        .format(calendar.getTime()));

輸出為:

2016年8月15日 下午2:15

DateFormat的工廠方法里,我們沒看到TimeZone參數,不過,DateFormat提供了一個setter方法,可以設置TimeZone:

public void setTimeZone(TimeZone zone)

DateFormat雖然比較方便,但如果我們要對字元串格式有更精確的控制,應該使用SimpleDateFormat這個類。

SimpleDateFormat

SimpleDateFormat是DateFormat的子類,相比DateFormat,它的一個主要不同是,它可以接受一個自定義的模式(pattern)作為參數,這個模式規定了Date的字元串形式。先看個例子:

Calendar calendar = Calendar.getInstance();
//2016-08-15 14:15:20
calendar.set(2016, 07, 15, 14, 15, 20);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 E HH時mm分ss秒");
System.out.println(sdf.format(calendar.getTime()));

輸出為:

2016年08月15日 星期一 14時15分20秒 

SimpleDateFormat有個構造方法,可以接受一個pattern作為參數,這裡pattern是:

yyyy年MM月dd日 E HH時mm分ss秒

pattern中的英文字元a-z和A-Z表示特殊含義,其他字元原樣輸出,這裡:

  • yyyy:表示四位的年
  • MM:表示月,兩位數表示
  • dd:表示日,兩位數表示
  • HH:表示24小時制的小時數,兩位數表示
  • mm:表示分鐘,兩位數表示
  • ss:表示秒,兩位數表示
  • E:表示星期幾 

這裡需要特意提醒一下,hh也表示小時數,但表示的是12小時制的小時數,而a表示的是上午還是下午,看代碼:

Calendar calendar = Calendar.getInstance();
//2016-08-15 14:15:20
calendar.set(2016, 07, 15, 14, 15, 20);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss a");
System.out.println(sdf.format(calendar.getTime()));

輸出為:

2016/08/15 02:15:20 下午

更多的特殊含義可以參看SimpleDateFormat的Java文檔。如果想原樣輸出英文字元,可以用單引號括起來。

除了將Date轉換為字元串,SimpleDateFormat也可以方便的將字元轉化為Date,看代碼:

String str = "2016-08-15 14:15:20.456";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
try {
    Date date = sdf.parse(str);
    SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy年M月d h:m:s.S a");
    System.out.println(sdf2.format(date));
} catch (ParseException e) {
    e.printStackTrace();
}

輸出為:

2016年8月15 2:15:20.456 下午

代碼將字元串解析為了一個Date對象,然後使用另外一個格式進行了輸出,這裡SSS表示三位的毫秒數。

需要註意的是,parse會拋出一個受檢異常(checked exception),異常類型為ParseException,調用者必須進行處理。

局限性

至此,關於Java 1.8之前的日期和時間相關API的主要內容,我們就介紹的差不多了,這裡我們想強調一下這些API的一些局限性。

Date中的過時方法

Date中的方法參數與常識不符合,過時方法標記容易被人忽略,產生誤用。比如說,看如下代碼:

Date date = new Date(2016,8,15);
System.out.println(DateFormat.getDateInstance().format(date));

想當然的輸出為2016-08-15,但其實輸出為:

3916-9-15

之所以產生這個輸出,是因為,Date構造方法中的year表示的是與1900年的差,month是從0開始的。

Calendar操作比較啰嗦臃腫

Calendar API的設計不是很成功,一些簡單的操作都需要多次方法調用,寫很多代碼,比較啰嗦臃腫。

另外,Calendar難以進行比較複雜的日期操作,比如,計算兩個日期之間有多少個月,根據生日計算年齡,計算下個月的第一個周一等。

下一節,我們會介紹Joda-Time,相比Calendar,Joda-Time要簡潔方便的多。

DateFormat的線程安全性

DateFormat/SimpleDateFormat不是線程安全的,關於線程概念,後續文章我們會詳解,這裡簡單說明一下,多個線程同時使用一個DateFormat實例的時候,會有問題,因為DateFormat內部使用了一個Calendar實例對象,多線程同時調用的時候,這個Calendar實例的狀態可能就會紊亂。

解決這個問題大概有以下方案:

  • 每次使用DateFormat都新建一個對象
  • 使用線程同步
  • 使用ThreadLocal
  • 使用Joda-Time,Joda-Time是線程安全的

後續文章我們再介紹線程同步和ThreadLocal。

小結

本節介紹了Java中(1.8之前)的日期和時間相關API,Date表示時刻,與年月日無關,Calendar表示日曆,與時區和Locale相關,可進行各種運算,是日期時間操作的主要類,DateFormat/SimpleDateFormat在Date和字元串之間進行相互轉換。

這些API存在著一些不足,操作比較複雜,代碼比較臃腫,還有線程安全的問題,實際中一個常用的第三方庫是Joda-Time,下一節,讓我們一起來看下。

----------------

未完待續,查看最新文章,敬請關註微信公眾號“老馬說編程”(掃描下方二維碼),從入門到高級,深入淺出,老馬和你一起探索Java編程及電腦技術的本質。用心寫作,原創文章,保留所有版權。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 獲取JavaScript 的時間使用內置的Date函數完成 var mydate = new Date();mydate.getYear(); //獲取當前年份(2位)mydate.getFullYear(); //獲取完整的年份(4位,1970-????)mydate.getMonth(); // ...
  • 作業:編寫登陸介面 輸入用戶名密碼 認證成功後顯示歡迎信息 輸錯三次後鎖定 ...
  • 1、自定義命令:將自定義命令保存在app/Console/Commands,也可以在composer.json文件配置自動載入,自由選擇想要放置的地方 php artisan make:console ShowGreet​(創建腳本來編寫命令,腳本存在app/Console/Commands)-》在 ...
  • 1、Ajax 特點: 局部刷新、提高用戶的體驗度,數據從伺服器商載入 2、AJax的技術組成 不是新技術,而是之前技術的整合 Ajax: Asynchronous Javascript And Xml; (非同步的JavaScript和XML) 包括的技術:JavaScript、XML、CSS、XML ...
  • ...
  • Maven+springboot+阿裡大於簡訊驗證服務 糾結點:Maven庫沒有sdk,需要解決 Maven打包找不到相關類,需要解決 ps:最近好久沒有寫點東西了,項目太緊,今天來一篇 一、本文簡介 在工作當中的項目中,我遇到過這種情況,公司架構選型SpringBoot ,這是個基於Maven構建 ...
  • 一.延遲載入的概念 當Hibernate從資料庫中載入某個對象時,不載入關聯的對象,而只是生成了代理對象,獲取使用session中的load的方法(在沒有改變lazy屬性為false的情況下)獲取到的也是代理對象,所以在上面這幾種場景下就是延遲載入。 二.理解立即載入的概念 當Hibernate從數 ...
  • 問:有了springMVC,為什麼還要用servlet?有了servlet3的註解,為什麼還要使用ServletRegistrationBean註入的方式? 使用場景:在有些場景下,比如我們要使用hystrix-dashboard,這時候就需要註入HystrixMetricsStreamServle ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...