前言
Java8釋出,已有數年之久,但是發現很多人都還是堅持著用SimpleDateFormat和Date進行時間操作。SimpleDateFormat這個類不是執行緒安全的,在使用的時候稍不注意,就會產生致命的問題。Date這個類,是可以重新設定時間的,這對於一些類內部的屬性來說,是非常不安全的。
SimpleDateFormat是執行緒不安全的類
在阿里巴巴規約手冊裡,強制規定SimpleDateFormat是執行緒不安全的類,當定義為靜態變數時,必須加鎖處理。忽略執行緒安全問題,正是大多數Java初學者在進行時間轉化時容易踩坑的點。
Date屬性可以重新設定時間
比如有User.java如下:
public class User {
private String username;
private Date birthday;
public User(String username, Date birthday) {
this.username = username;
this.birthday = birthday;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
複製程式碼
我們例項化該User
public class Main {
public static void main(String[] args) {
User user = new User("happyjava", new Date());
}
}
複製程式碼
這當然沒什麼問題,但是我可以通過user.getBirthday()方法獲取到birthday的引用,從而修改直接修改birthday的值。如下:
public static void main(String[] args) {
User user = new User("happyjava", new Date());
System.out.println(user.getBirthday());
Date birthday = user.getBirthday();
birthday.setTime(11111111L);
System.out.println(user.getBirthday());
}
複製程式碼
輸出結果如下:
這裡可以看到,user物件的birthday屬性被修改掉了。這也是Date物件的弊端所在,我們可以通過改寫getter方法,使它返回一個新的Date物件即可解決,如下:
public Date getBirthday() {
// return birthday;
return new Date(birthday.getTime());
}
複製程式碼
切記這裡是不可以用clone方法來生成返回一個新的Date物件的,因為Date類可以被繼承,你不能確定呼叫者是否給birthday設定了一個Date的子類。
Java8提供的新的時間類庫LocalDateTime
Java8提供了LocalDateTime來替代傳統的Date來處理時間,下面,我們就來探討下這個類庫的使用方法吧。
1.獲取當前時間
可以通過 LocalDateTime localDateTime = LocalDateTime.now();方法來獲取當前時間,測試如下:
@Test
public void testNow() {
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime);
}
複製程式碼
輸出結果
2019-05-06T22:25:07.309
複製程式碼
2.根據時間戳初始化時間
@Test
public void testNewFromTimestamp() {
Instant instant = Instant.ofEpochMilli(System.currentTimeMillis());
LocalDateTime dateTime = LocalDateTime.ofInstant(instant, ZoneId.of("+8"));
System.out.println(dateTime);
}
複製程式碼
這裡的+8意思是東八區,下同。
輸出結果:
2019-05-06T22:27:34.567
複製程式碼
3.根據字串獲取時間
可以使用LocalDateTime.parse方法對字串進行轉化成時間,如果不傳pattern,預設是2019-05-06T11:16:12.361格式。
@Test
public void testNewFromString() {
// 1.預設格式 2019-05-06T11:16:12.361
String dateStr = "2019-05-06T11:16:12.361";
LocalDateTime localDateTime = LocalDateTime.parse(dateStr);
System.out.println(localDateTime);
// 2. 自定義格式
String pattern = "yyyy-MM-dd HH:mm:ss";
dateStr = "2019-01-01 12:12:12";
localDateTime = LocalDateTime.parse(dateStr, DateTimeFormatter.ofPattern(pattern));
System.out.println(localDateTime);
}
複製程式碼
輸出結果:
2019-05-06T11:16:12.361
2019-01-01T12:12:12
複製程式碼
4.時間轉化成字串
可以通過DateTimeFormatter的format方法,將LocalDateTime轉化成字串。
@Test
public void testToString() {
LocalDateTime now = LocalDateTime.now(ZoneId.of("+8"));
String pattern = "yyyy-MM-dd HH:mm:ss";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
String format = formatter.format(now);
System.out.println(format);
}
複製程式碼
輸出結果:
2019-05-06 22:33:03
複製程式碼
5.LocalDateTime轉時間戳
@Test
public void testDateToTimeMillis() {
LocalDateTime dateTime = LocalDateTime.now();
long epochMilli = dateTime.toInstant(ZoneOffset.of("+8")).toEpochMilli();
System.out.println(epochMilli);
}
複製程式碼
輸出結果:
1557153504304
複製程式碼
總結
因為DateTimeFormatter是執行緒安全的,所以在實際使用LocalDateTime的時候,可以把DateTimeFormatter定義成靜態常量的方式進行使用。以上列舉了比較常用的時間操作,LocalDateTime還可以做很多事情,這個就讓讀者自行去挖掘吧。我自己封裝了個LocalDateTime工具類,只做過簡單的自測,大家可以參考一下:
package happy.localdatetime;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
/**
* @author Happy
*/
public class DateTimeUtils {
private DateTimeUtils() {
}
private final static String COMMON_PATTERN = "yyyy-MM-dd HH:mm:ss";
private final static DateTimeFormatter COMMON_FORMATTER = DateTimeFormatter.ofPattern(COMMON_PATTERN);
private final static ZoneOffset DEFAULT_ZONE_OFFSET = ZoneOffset.of("+8");
/**
* 預設 yyyy-MM-dd HH:mm:ss 格式
*/
public static String dateToString(LocalDateTime dateTime) {
assert dateTime != null;
return COMMON_FORMATTER.format(dateTime);
}
/**
* 預設 yyyy-MM-dd HH:mm:ss 格式
*/
public static LocalDateTime stringToDate(String dateStr) {
assert dateStr != null;
return LocalDateTime.parse(dateStr, COMMON_FORMATTER);
}
public static String dateToString(LocalDateTime dateTime, DateTimeFormatter formatter) {
assert dateTime != null;
return formatter.format(dateTime);
}
public static LocalDateTime stringToDate(String dateStr, DateTimeFormatter formatter) {
assert dateStr != null;
return LocalDateTime.parse(dateStr, formatter);
}
public static long dateToTimeMillis(LocalDateTime dateTime) {
assert dateTime != null;
return dateTime.toInstant(DEFAULT_ZONE_OFFSET).toEpochMilli();
}
public static LocalDateTime timeMillisToDate(long timeMillis) {
Instant instant = Instant.ofEpochMilli(timeMillis);
return LocalDateTime.ofInstant(instant, DEFAULT_ZONE_OFFSET);
}
public static void main(String[] args) {
String s = dateToString(LocalDateTime.now());
System.out.println(s);
System.out.println();
String dateStr = "2019-01-01 12:12:12";
LocalDateTime localDateTime = stringToDate(dateStr);
System.out.println(localDateTime);
System.out.println();
System.out.println(dateToTimeMillis(localDateTime));
System.out.println();
System.out.println(timeMillisToDate(System.currentTimeMillis()));
}
}
複製程式碼