HashMap匿名初始化和日期的時區問題

擊水三千里發表於2019-01-15

實際開發中遇到了一些奇怪的問題在這裡記錄下:

一、HashMap初始化

先來看段程式碼:

import com.google.gson.Gson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

 
public class Test  {

    public static void main(String[] args) {
        Map<String,Object> map  = new HashMap<String, Object>(){{
            put("key1","val1");
            put("key2","val2");
        }};
        String str = new Gson().toJson(map);
        System.out.println("str: "+str);
    }

}

輸出:

str: null

 初始化HashMap時,這裡的雙括號到底什麼意思代表什麼呢?先來看段程式碼

public class Test {
 
 public Test() {
  System.out.println("Constructor called:構造器被呼叫");
 }
 static {
  System.out.println("Static block called:靜態塊被呼叫");
 }
 {
  System.out.println("Instance initializer called:例項初始化塊被呼叫");
 }
 public static void main(String[] args) {
  new Test();
  System.out.println("=======================");
  new Test();
 }
}

輸出:

 Static block called:靜態塊被呼叫
 Instance initializer called:例項初始化被呼叫
 Constructor called:構造器被呼叫
 =======================
 Instance initializer called:例項初始化被呼叫
 Constructor called:構造器被呼叫

也就是說第一層括弧實際是定義了一個匿名內部類 (Anonymous Inner Class),第二層括弧實際上是一個例項初始化塊 (instance initializer block),這個塊在內部匿名類構造時被執行。這個塊之所以被叫做“例項初始化塊”是因為它們被定義在了一個類的例項範圍內。
上面程式碼如果是寫在 Test 類中,編譯後你會看到會生成 Test$1.class 檔案,反編譯該檔案內容:

import java.util.HashMap;
 
class Test$1 extends HashMap // 建立了一個 HashMap 的子類
 {
 Test$1()
 {    // 第二個 {} 中的程式碼放到了構造方法中去了 
     put("key1","val1");
     put("key2","val2");
 }
 }

匿名內部類寫法的潛在問題
    文章開頭提到的寫法的好處很明顯就是一目瞭然。這裡來羅列下此種方法的壞處,如果這個物件要序列化,可能會導致序列化失敗。
  1.此種方式是匿名內部類的宣告方式,所以引用中持有著外部類的引用。所以當序列化這個集合時外部類也會被不知不覺的序列化,當外部類沒有實現serialize介面時,就會報錯。

 2.上例中,其實是宣告瞭一個繼承自HashMap的子類。然而有些序列化方法,例如要通過Gson序列化為json,或者要序列化為xml時,類庫中提供的方式,是無法序列化HashSet或者HashMap的子類的,從而導致序列化失敗。解決辦法:重新初始化為一個HashMap物件

new HashMap(map)

這種寫法,推而廣之,在初始化 ArrayList、Set 的時候都可以這麼玩,比如你還可以這麼玩:

 List<String> names = new ArrayList<String>() {
 {
 for (int i = 0; i < 10; i++) {
  add("A" + i);
 }
 }
 };
 System.out.println(names.toString()); // [A0, A1, A2, A3, A4, A5, A6, A7, A8, A9]

二、時區問題 

背景:在操作日期時經常會出現比資料庫多或少8小時的情況
原因:不同軟體或系統的預設時區不一致導致的

幾個時間名詞: 

  1. GMT:格林威治標準時間 
  2. UTC:世界協調時間 
  3. DST:夏日節約時間 
  4. CST:中國標準時間 

其中GMT時間可以近似認為和UTC時間是相等的,但從精度上來說UTC時間更精確。其誤差值必須保持在0.9秒以內 

CST= GMT + 8 =UTC + 8 

2.1、ES的當前時區

預設儲存時間的格式是UTC時間,如果我們查詢es然後獲取時間日期預設的資料,會發現跟當前的時間差8個小時,這其實是正常的,因為es預設儲存是用的UTC時間,所以我們需要做的就是讀取long型時間戳,然後重新格式化成下面的時間戳,即可獲得正確的時間 :

yyyy-MM-dd HH:mm:ss 

2.2、Mysql 當前時區

檢視mysql當前時間,當前時區

show variables like "%time_zone%";

MySQL中datetime和timestamp的區別及使用

1.對於timestamp,它把客戶端插入的時間從當前時區轉化為UTC(世界標準時間)進行儲存。查詢時,將其又轉化為客戶端當前時區進行返回;而對於datetime,不做任何改變,基本上是原樣輸入和輸出。

2.datetime佔用8個位元組,timestamp佔用4個位元組。timestamp利用率更高。

3.二者範圍不一樣。timestamp範圍:‘1970-01-01 00:00:01.000000’ 到 ‘2038-01-19 03:14:07.999999’; datetime範圍:’1000-01-01 00:00:00.000000’ 到 ‘9999-12-31 23:59:59.999999’。原因是,timestamp佔用4位元組,能表示最大的時間毫秒為2的31次方減1,也就是2147483647,換成時間剛好是2038-01-19 03:14:07.999999。

 

2.3、Linux當前時區

檢視當前時區
命令 : "date -R"

.Java8中基本只能通過當前位置所在城市名來獲取時區 

ZoneId defaultZone = ZoneId.systemDefault(); System.out.println(defaultZone); 

 

相關文章