1 新的 Date/Time API
Java 8引入了新的Date和Time的API,從而修復了一些舊API的缺陷。
這些缺陷主要有:
- 不是線程安全的:java.util.Date不是線程安全的,因此開發者在以前用這些API的時候必須要注意併發的情況。而新的API是不可變的,並且沒有setter方法。
- 匱乏的設計:默認日期的年從1900年開始、月份從1開始,並且日期從0開始,它們缺乏統一性。舊的API對於日期的操作缺乏直接的方法。新的API則對於這些操作提供了許多的設定方法。
- 較難的時區處理:之前,開發者需要寫大量的代碼來解決時區的問題。新的API則簡化了這個步驟。
Java 8在java.time包內引入了這些新的API。下面是一些比較重要的類: - Local:簡化的date/time API,沒有時區處理的特性。
- Zoned:定製的date/time API,用於處理多時區的情況。
1.1 一個本地日期時間的例子
Java 8提供了LocalDate、LocalTime和LocalDataTime類來簡化不需要時區的情況下的開發工作。下面我們通過一個例子來學習它們。
以下是NewFeaturesTester.java類中應當輸入的代碼:
public class NewFeaturesTester {
public static void main(String args[]){
NewFeaturesTester tester = new NewFeaturesTester();
tester.testLocalDateTime();
}
public void testLocalDateTime(){
// 獲得當前的日期和時間
LocalDateTime currentTime = LocalDateTime.now();
System.out.println("current date and time: " + currentTime);
// 輸出當前時間的本地值(本時區)
LocalDate date1 = currentTime.toLocalDate();
System.out.println("local date: " + date1);
Month month = currentTime.getMonth();
int day = currentTime.getDayOfMonth();
int seconds = currentTime.getSecond();
// 由當前時間對象獲得各個字段,輸出結果
System.out.println("month: " + month +"day: " + day +"seconds: " + seconds);
// 由當前時間附帶月份和年再輸出
LocalDateTime date2 = currentTime.withDayOfMonth(10).withYear(2012);
System.out.println("date 2: " + date2);
// 輸出2016年聖誕節的日期
LocalDate date3 = LocalDate.of(2016, Month.DECEMBER, 25);
System.out.println("date 3: " + date3);
// 輸出新聞聯播的開始時間
LocalTime date4 = LocalTime.of(19, 00);
System.out.println("date 4: " + date4);
// 轉化爲字符串,再輸出
LocalTime date5 = LocalTime.parse("20:15:30");
System.out.println("date 5: " + date5);
}
}
1.2 一個帶時區的 Date/Time API例子
具有時區功能的日期時間API被用於需要考慮時區信息的情況。同樣的,我們通過一個例子來使用它們。
public class NewFeaturesTester {
public static void main(String args[]){
NewFeaturesTester tester = new NewFeaturesTester();
tester.testZonedDateTime();
}
public void testZonedDateTime(){
// 將字符串代表的時區信息轉化
ZonedDateTime date1 = ZonedDateTime.parse("2016-04-20T19:22:15+01:30[Europe/Paris]");
System.out.println("date1: " + date1);
// 設定地區ID爲亞洲的加爾各答(位於印度),並輸出
ZoneId id = ZoneId.of("Asia/Kolkata");
System.out.println("ZoneId: " + id);
// 獲得系統默認的當前地區並輸出
ZoneId currentZone = ZoneId.systemDefault();
System.out.println("CurrentZone: " + currentZone);
}
}
1.3 一個枚舉計時單位的例子
在java.time.temporal.ChronoUnit包內,添加了枚舉常量,來代替舊的API裏用整數值代表的日期值。
public class NewFeaturesTester {
public static void main(String args[]){
NewFeaturesTester tester = new NewFeaturesTester();
tester.testChromoUnits();
}
public void testChromoUnits(){
// 獲得當前的日期並輸出
LocalDate today = LocalDate.now();
System.out.println("Current date: " + today);
// 在當前日期的基礎上增加兩週時間再輸出
LocalDate nextWeek = today.plus(2, ChronoUnit.WEEKS);
System.out.println("two weeks after now: " + nextWeek);
// 在當前日期的基礎上增加6個月的時間再輸出
LocalDate nextMonth = today.plus(6, ChronoUnit.MONTHS);
System.out.println("6 months after now: " + nextMonth);
// 在當前日期的基礎上增加5年的時間再輸出
LocalDate nextYear = today.plus(5, ChronoUnit.YEARS);
System.out.println("5 years after now: " + nextYear);
// 在當前日期的基礎上增加20年的時間再輸出(一個Decade爲10年)
LocalDate nextDecade = today.plus(2, ChronoUnit.DECADES);
System.out.println("20 years after now: " + nextDecade);
}
}
1.4 一個時間段的例子
在Java 8中,還有兩個特殊的類用於處理一些特殊的時間問題:
- Period:該類用於處理日期相關的時間段
- Duration:該類用於處理時間相關的時間段
public class NewFeaturesTester {
public static void main(String args[]){
NewFeaturesTester tester = new NewFeaturesTester();
tester.showPeriod();
tester.showDuration();
}
public void showPeriod(){
// 獲得當前的日期
LocalDate date1 = LocalDate.now();
System.out.println("Current date: " + date1);
// 在當前日期的基礎上增加一個月時間
LocalDate date2 = date1.plus(1, ChronoUnit.MONTHS);
System.out.println("Next month: " + date2);
// 用between方法計算兩個日期直接的間隔(稱之爲Period)
Period period = Period.between(date2, date1);
System.out.println("Period: " + period);
}
public void showDuration(){
LocalTime time1 = LocalTime.now();
Duration fiveHours = Duration.ofHours(5);
LocalTime time2 = time1.plus(fiveHours);
// 對應的,用between方法顯示兩個時間直接的間隔(稱之爲Duration)
Duration duration = Duration.between(time1, time2);
System.out.println("Duration: " + duration);
}
}
1.5 一個時間調節器的例子
TemporalAdjuster是用於計算日期的數學工具,比如說你可以通過下面的例子,獲得“下個月的第二個週日”這樣的計算出來的日期。:
public class NewFeaturesTester {
public static void main(String args[]){
NewFeaturesTester tester = new NewFeaturesTester();
tester.applyAdjusters();
}
public void applyAdjusters(){
// 獲得當前的日期
LocalDate date1 = LocalDate.now();
System.out.println("current date: " + date1);
// 計算下週一的日期並輸出
LocalDate nextMonday = date1.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
System.out.println("next monday on : " + nextMonday);
// 獲得下個月的第二個週期的日期並輸出
LocalDate date2 = date1.plus(1, ChronoUnit.MONTHS);
LocalDate firstInMonth = LocalDate.of(date2.getYear(),date2.getMonth(), 1);
LocalDate secondSunday = firstInMonth.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY)).with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
System.out.println("second sunday of next month : " + secondSunday);
}
}
1.6 向後兼容能力
爲了保持向後兼容的能力,原始的Date和Calendar對象添加了一個toInstant方法。該方法可以將其轉換爲新API下的對象。當然,還可以用ofInstant方法來獲得LocalDateTime和ZonedDataTime對象。
public class NewFeaturesTester {
public static void main(String args[]){
NewFeaturesTester tester = new NewFeaturesTester();
tester.applyBackwardCompatability();
}
public void applyBackwardCompatability(){
// 獲得當前日期並輸出
Date currentDate = new Date();
System.out.println("Current date: " + currentDate);
// 獲得當前日期的實例(以毫秒的形式)
Instant now = currentDate.toInstant();
ZoneId currentZone = ZoneId.systemDefault();
// 用ofInstant方法獲得實例
LocalDateTime localDateTime = LocalDateTime.ofInstant(now, currentZone);
System.out.println("Local date: " + localDateTime);
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(now, currentZone);
System.out.println("Zoned date: " + zonedDateTime);
}
}
2 Base64編碼
在Java 8中,內置了Base64編解碼相關的特性。我們可以在Java 8中使用下面三種類型的Base64編解碼:
- 簡易模式:輸出是完全按照A-Za-z0-9+/字符集映射的。編碼不會自己增加輸出行,解碼器也不會接受任何超出A-Za-z0-9+/範圍的內容。
- URL模式:輸出基於A-Za-z0-9+/的映射,但對於URL和文件名是安全的。
- MIME模式:輸出對於MIME類型的內容是友好的。如果超過76個字符,則會換行輸出,並且換行符\n之後會自動添加一個\r。如果某行沒有\r則說明輸出的內容已經結束。
2.1 Base64的內部類和方法
Base64相關的內部類有:
- Base64.Encoder:這是一個靜態類。實現了Base64的編碼功能,格式遵循了RFC 4648和RFC 2045標準。
- Base64.Decoder:也是一個靜態類。實現了Base64的解碼功能。
相關的方法有: - getEncoder():該方法返回一個使用基本Base64編碼格式的Encoder對象。相反的解碼方法是getDecoder()。
- getUrlEncoder():該方法返回一個使用URL類型的Base64編碼格式的Encoder對象。相反的解碼方法是getUrlDecoder()。
- getMimeEncoder():該方法返回一個使用MIME類型的Base64編碼格式的Encoder對象。相反的解碼方法是getMimeDecoder()。
更多的方法你可以查閱Java 8的官方手冊。
2.2 一個Base64編解碼的例子
使用Java 8的API,進行Base64編解碼。
public class NewFeaturesTester {
public static void main(String args[]){
try {
// 使用基本的Base64編碼
String base64encodedString = Base64.getEncoder().encodeToString("Shiyanlou.com".getBytes("utf-8"));
System.out.println("Basic base64 encoding:" + base64encodedString);
// 解碼並輸出結果
byte[] base64decodedBytes = Base64.getDecoder().decode(base64encodedString);
System.out.println("Original content: " + new String(base64decodedBytes, "utf-8"));
// 使用URL類型的Base64編碼
base64encodedString = Base64.getUrlEncoder().encodeToString("Shiyanlou.com".getBytes("utf-8"));
System.out.println("URL base64 encoding:" + base64encodedString);
// MIME類型的Base64編碼
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 10; ++i) {
stringBuilder.append(UUID.randomUUID().toString());
}
byte[] mimeBytes = stringBuilder.toString().getBytes("utf-8");
String mimeEncodedString = Base64.getMimeEncoder().encodeToString(mimeBytes);
System.out.println("MIME base64 encoding:" + mimeEncodedString);
}catch(UnsupportedEncodingException e){
// 捕獲異常並輸出
System.out.println("Exception:" + e.getMessage());
}
}
}
最全的Java 8新功能說明位於Java的官方網站,你可以通過閱讀《What’s New in JDK 8》來了解它們。