JAVA 8 新特性
Java 8 應該是目前項目中使用最多的版本,之前有使用過它的一些新特性,了解一些基本的用法,但是對于一些理論性的概念不是很清楚,最近看了一些教程和博客,收獲很大,在這里記錄一下。
介紹
Java 8 新增了非常多的新特性,包括一些數據結構的優(yōu)化,JVM的優(yōu)化,這里只記錄一些日常中用到的新特性:
- Lambda表達式
- 方法引用
- 函數式接口
- 默認方法
- Stream
- Optional
- 新的日期API
1. Lambda
Lambda 允許把函數作為一個方法的參數(函數作為參數傳遞進方法中)。
使用 Lambda 表達式可以使代碼變的更加簡潔緊湊。
-
先來感受一下從 匿名內部類 到 Lambda 的轉換
-
案例一
//匿名內部類 Runnable r = new Runnable() { @Override public void run() { System.out.println("Hello World!"); } }; //Lambda 表達式 Runnable r2 = () -> System.out.println("Hello World!"); r.run(); r2.run();
-
案例二
//使用匿名內部類作為參數傳遞 TreeSet<String> t = new TreeSet<>(new Comparator<String>() { @Override public int compare(String o1, String o2) { return Integer.compare(o1.length(),o2.length()); } }); //Lambda 表達式作為參數傳遞 TreeSet<String> t2 = new TreeSet<>((o1, o2) -> Integer.compare(o1.length(), o2.length()));
通過上面兩個案例可以看出,Lambda 是一個匿名函數,我們可以把 Lambda 表達式理解為是一段可以傳遞的代碼(將代碼像數據一樣進行傳遞)。Lambda 明顯的減少了代碼量。
-
-
Lambda表達式語法
- 語法格式
//引入了新的語法元素和操作符,"->" 箭頭操作符將表達式分成了兩部分 //左邊 表達式所需要的參數 右邊 表達式將要執(zhí)行的功能 (parameters) -> expression (parameters) -> { statements;}
-
語法特性
- 可選類型聲明:不需要聲明參數類型,編譯器可以統(tǒng)一識別參數值。
- 可選的參數圓括號:一個參數無需定義圓括號,但多個參數需要定義圓括號。
- 可選的大括號:如果主體包含了一個語句,就不需要使用大括號。
- 可選的返回關鍵字:如果主體只有一個表達式返回值則編譯器會自動返回值,使用大括號時需要指明表達式return的值。
2. 函數式接口
就是一個有且僅有一個抽象方法,但是可以有多個非抽象方法的接口。
- 自定義函數式接口
@FunctionalInterface
public interface MyShow {
public String getShow();
}
//函數式接口中使用泛型
@FunctionalInterface
public interface MyShow02<T> {
public T getShow(T t);
}
- Lambda 作為參數傳遞時,接收的參數類型必須是與 Lambda 兼容的函數式接口
public static String myShow02(MyShow02<String> myShow02,String s){
return myShow02.getShow(s);
}
//Lambda作為參數傳遞
String afssd = myShow02(a -> a.toUpperCase(), "afssd");
System.out.println(afssd);
-
Java 內置的四大核心函數式接口
函數式接口 參數類型 返回類型 用途 Consumer<T>
消費型接口T void 對類型為T的對象應用操作。
包含方法: void accept(T t);Supplier<T>
供給型接口無 T 返回類型為T的對象。
包含方法:T get();Function<T,R>
函數型接口T R 對類型為T的對象應用操作,并返回結果。結果是R類型的對象。
包含方法:R apply(T t);Predicate<T>
斷定型接口T boolean 確定類型為T的對象是否滿足某約束,并返回boolean 值。
包含方法boolean test(T t); -
其它接口
函數式接口 參數類型 返回類型 用途 BiFunction<T, U, R> T, U R 對類型為 T, U 參數應用操作,返回 R 類型的結果。
包含方法為:R apply(T t, U u);UnaryOperator<T>
(Function子接口)T T 對類型為T的對象進行一元運算,并返回T類型的結果。
包含方法為T apply(T t);BinaryOperator<T>
(BiFunction 子接口)T T 對類型為T的對象進行二元運算,并返回T類型的結果。
包含方法為T apply(T t1, T t2);BiConsumer<T, U> T, U void 對類型為T, U 參數應用操作。包含方法為void accept(T t, U u); IntFunction<R>
LongFunction<R>
DoubleFunction<R>T int
long
double分 別 計 算 int 、 long 、double值的函數 IntFunction<R>
LongFunction<R>
DoubleFunction<R>int
long
doubleR R 參數分別為int、long、double 類型的函數
3. 方法引用
當要傳遞給Lambda體的操作,已經有實現的方法了,可以使用方法引用!
方法引用與Lambda結合使用可以使代碼更加簡潔,緊湊。
-
語法
- 使用操作符
" :: "
,將方法名和對象或類的名字分隔開來。
- 使用操作符
-
使用場景
- Lambda表達式的主體僅包含一個表達式,且該表達式僅調用一個已經存在的方法
- 方法引用所使用的方法的入參和返回值與Lambda表達式實現的函數式接口的入參和返回值一致
-
方法引用
-
方法引用的四種形式
1. 靜態(tài)方法引用 : ClassName :: staticMethodName 2. 構造器引用 : ClassName :: new 3. 特定類的任意對象的實例方法引用: ClassName :: instanceMethodName 4. 特定對象的實例方法引用 : object :: instanceMethodName
-
案例
-
靜態(tài)方法引用
Lambda表達式主體僅調用某個類的靜態(tài)方法時使用。
class Java8Test03 { public static void main(String args[]) { //使用Lambda表達式 Arrays.asList("張三1", "李四1", "王二1").stream().forEach((x) -> Java8Test03.show(x)); //使用靜態(tài)方法引用 Arrays.asList("張三2", "李四2", "王二2").stream().forEach(Java8Test03::show); } //靜態(tài)方法 public static void show(String name) { System.out.println(name + "show!"); } }
張三1show! 李四1show! 王二1show! 張三2show! 李四2show! 王二2show!
-
構造器引用
Lambda表達式主體僅調用某個類構造方法返回實例時使用。
class Java8Test04 { public static void main(String args[]) { //使用Lambda表達式 Function<Integer, Integer[]> fun = (n) -> new Integer[n]; //使用構造器引用 Function<Integer, Integer[]> fun2 = Integer[]::new; } }
-
特定類的任意對象的實例方法引用
- 方法引用所使用方法的入參和返回值要與Lambda表達式實現的函數式接口的入參和返回值一致。
- Lambda表達式的第一個入參為實例方法的調用者,后面的入參與實例方法一致。
class MyUser { private String name; private Integer age; public void setNameAndAge(String name, Integer age) { this.name = name; this.age = age; System.out.println("name: " + name + "age: " + age); } public static void main(String args[]) { //lambda表達式的用法: TestInterface testInterface1 = (myUser, name, age) -> myUser.setNameAndAge(name, age); testInterface1.set(new MyUser(), "張三", 18); //類的任意對象的實例方法引用的用法: TestInterface testInterface2 = MyUser::setNameAndAge; testInterface2.set(new MyUser(), "李四", 25); } @FunctionalInterface interface TestInterface { // 注意:入參比User類的setNameAndAge方法多1個MyUser對象,除第一個外其它入參類型一致 public void set(MyUser myUser, String name, Integer age); } }
name: 張三age: 18 name: 李四age: 25
-
特定對象的實例方法引用
Lambda表達式主體中僅調用了某個對象的某個實例方法時使用。
class Java8Test05 { public static void main(String args[]) { //使用Lambda表達式 Arrays.asList("張三1", "李四1", "王二1").stream().forEach((x) -> Java8Test03.show(x)); //使用靜態(tài)方法引用 Arrays.asList("張三2", "李四2", "王二2").stream().forEach(Java8Test03::show); } //實例方法 public void show(String name) { System.out.println(name + "show!"); } }
張三1show! 李四1show! 王二1show! 張三2show! 李四2show! 王二2show!
-
-
4. 默認方法
默認方法就是接口可以有實現方法,而且不需要實現類去實現這個方法。
我們只需要在方法名前加default
關鍵字即可標記該方法為默認方法。
- 為什么要有這個特性?
首先,之前的接口是個雙刃劍,
好處是面向抽象而不是面向具體編程,
缺陷是,當需要修改接口時候,需要修改全部實現該接口的類,目前的 java 8 之前的集合框架沒有 foreach 方法,
通常能想到的解決辦法是在JDK里給相關的接口添加新的方法及實現。
然而,對于已經發(fā)布的版本,是沒法在給接口添加新方法的同時不影響已有的實現。所以引進的默認方法。
他們的目的是為了解決接口的修改與現有的實現不兼容的問題。
-
語法
public interface TestInterface { default void print() { System.out.println("我是默認方法。"); } }
-
多個默認方法
我們知道一個類可以實現多個接口,那么如果恰巧這些接口擁有相同的默認方法,實現類在調用默認方法時,會選擇使用哪個接口的默認方法呢?
為了解決這種情況的沖突,Java 8 提供了以下三條原則:
- 類中的方法優(yōu)先級最高,類或父類中申明的方法優(yōu)先級高于任何聲明為默認方法的優(yōu)先級。
- 如果第一條無法判斷,那么子接口的優(yōu)先級更高:方法簽名相同時,優(yōu)先選擇擁有最具實現的默認方法的接口,即B繼承了A,那么B比A更具體。
- 最后,如果還是無法判斷,繼承了多個接口的類必須通過顯式覆蓋和調用期望的方法, 顯式地選擇使用哪一個默認方法的實現。
5. Stream
是數據渠道,用于操作數據源(集合、數組等)所生成的元素序列。
集合注重數據存儲,流注重計算!
-
注意
- Stream 自己不會存儲元素。
- Stream 不會改變源對象。相反,他們會返回一個持有結果的新Stream。
- Stream 操作是延遲執(zhí)行的。這意味著他們會等到需要結果的時候才執(zhí)行。
-
Stream操作的三個步驟
-
創(chuàng)建 Stream
一個數據源(如:集合、數組),獲取一個流。以下是獲取流的三種方式:
-
Java 8中的Collection接口被擴展,提供了兩個獲取流的方法
//返回一個順序流 default Stream<E> stream() { return StreamSupport.stream(spliterator(), false); } //返回一個并行流 (并行流就是把一個內容分成多個數據塊,并用不同的線程分別處理每個數據塊的流。) default Stream<E> parallelStream() { return StreamSupport.stream(spliterator(), true); }
-
Arrays 的靜態(tài)方法 stream() 可以獲取數組流
//返回一個流 public static <T> Stream<T> stream(T[] array) { return stream(array, 0, array.length); }
//重載形式,能夠處理對應基本類型的數組: public static IntStream stream(int[] array) { return stream(array, 0, array.length); } public static IntStream stream(int[] array, int startInclusive, int endExclusive) { return StreamSupport.intStream(spliterator(array, startInclusive, endExclusive), false); } public static LongStream stream(long[] array) { return stream(array, 0, array.length); } public static LongStream stream(long[] array, int startInclusive, int endExclusive) { return StreamSupport.longStream(spliterator(array, startInclusive, endExclusive), false); } public static DoubleStream stream(double[] array) { return stream(array, 0, array.length); } public static DoubleStream stream(double[] array, int startInclusive, int endExclusive) { return StreamSupport.doubleStream(spliterator(array, startInclusive, endExclusive), false); }
-
由值創(chuàng)建流
//可以使用靜態(tài)方法 Stream.of(), 通過顯示值創(chuàng)建一個流。它可以接收任意數量的參數。 public static<T> Stream<T> of(T t) { return StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false); }
-
由函數創(chuàng)建流:創(chuàng)建無限流
//迭代 public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {...} //生成 public static<T> Stream<T> generate(Supplier<T> s) {...}
-
-
中間操作
一個中間操作鏈,對數據源的數據進行處理
多個中間操作可以連接起來形成一個流水線,除非流水線上觸發(fā)終止操作,否則中間操作不會執(zhí)行任何的處理!而在終止操作時一次性全部處理,稱為“惰性求值”
-
篩選與切片
方法 描述 filter(Predicate p) 接收 Lambda , 從流中排除某些元素。 distinct() 篩選,通過流所生成元素的 hashCode() 和 equals() 去除重復元素 limit(long maxSize) 截斷流,使其元素不超過給定數量 skip(long n) 跳過元素,返回一個扔掉了前 n 個元素的流。若流中元素不足 n 個,則返回一個空流。與 limit(n) 互補 -
映射
方法 描述 map(Function f) 接收一個函數作為參數,該函數會被應用到每個元素上,并將其映射成一個新的元素。 mapToDouble(ToDoubleFunction f) 接收一個函數作為參數,該函數會被應用到每個元素上,產生一個新的 DoubleStream。 mapToInt(ToIntFunction f) 接收一個函數作為參數,該函數會被應用到每個元素上,產生一個新的 IntStream。 mapToLong(ToLongFunction f) 接收一個函數作為參數,該函數會被應用到每個元 flatMap(Function f) 接收一個函數作為參數,將流中的每個值都換成另一個流,然后把所有流連接成一個流 -
排序
方法 描述 sorted() 產生一個新流,其中按自然順序排序 sorted(Comparator comp) 產生一個新流,其中按比較器順序排序
-
-
-
中止操作(終端操作)
一個終止操作,執(zhí)行中間操作鏈,并產生結果
終端操作會從流的流水線生成結果。其結果可以是任何不是流的值,例如:List、Integer,甚至是 void 。
-
查找與匹配
方法 描述 allMatch(Predicate p) 檢查是否匹配所有元素 anyMatch(Predicate p) 檢查是否至少匹配一個元素 noneMatch(Predicate p) 檢查是否沒有匹配所有元素 findFirst() 返回第一個元素 findAny() 返回當前流中的任意元素 count() 返回流中元素總數 max(Comparator c) 返回流中最大值 min(Comparator c) 返回流中最小值 forEach(Consumer c) 內部迭代
(使用 Collection 接口需要用戶去做迭代,稱為外部迭代 ) -
歸約
方法 描述 reduce(T iden, BinaryOperator b) 可以將流中元素反復結合起來,得到一個值。返回 T reduce(BinaryOperator b) 可以將流中元素反復結合起來,得到一個值。返回 Optional<T> -
收集
方法 描述 collect(Collector c) 將流轉換為其他形式。接收一個 Collector接口的實現,用于給Stream中元素做匯總的方法
-
Collector 接口中方法的實現決定了如何對流執(zhí)行收集操作(如收集到 List、Set、Map)。但是 Collectors 實用類提供了很多靜態(tài)方法,可以方便地創(chuàng)建常見收集器實例,具體方法與實例如下表:
方法 | 返回類型 | 作用 | 示例 |
---|---|---|---|
toList | List<T> | 把流中元素收集到List | List<Employee> emps= list.stream().collect(Collectors.toList()); |
toSet | Set<T> | 把流中元素收集到Set | Set<Employee> emps= list.stream().collect(Collectors.toSet()); |
toCollection | Collection<T> | 把流中元素收集到創(chuàng)建的集合 | Collection<Employee>emps=list.stream().collect(Collectors.toCollection(ArrayList::new)); |
counting | Long | 計算流中元素的個數 | long count = list.stream().collect(Collectors.counting()); |
summingInt | Integer | 對流中元素的整數屬性求和 | inttotal=list.stream().collect(Collectors.summingInt(Employee::getSalary)); |
averagingInt | Double | 計算流中元素Integer屬性的平均值 | doubleavg= list.stream().collect(Collectors.averagingInt(Employee::getSalary)); |
summarizingInt | IntSummaryStatistics | 收集流中Integer屬性的統(tǒng)計值。如:平均值 | IntSummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Employee::getSalary)); |
joining | String | 連接流中每個字符串 | String str= list.stream().map(Employee::getName).collect(Collectors.joining()); |
maxBy | Optional<T> | 根據比較器選擇最大值 | Optional<Emp>max= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary))); |
minBy | Optional<T> | 根據比較器選擇最小值 | Optional<Emp> min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary))); |
reducing | 歸約產生的類型 | 從一個作為累加器的初始值開始,利用BinaryOperator與流中元素逐個結合,從而歸約成單個值 | inttotal=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum)); |
collectingAndThen | 轉換函數返回的類型 | 包裹另一個收集器,對其結果轉換函數 | inthow= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size)); |
groupingBy | Map<K, List<T>> | 根據某屬性值對流分組,屬性為K,結果為V | Map<Emp.Status, List<Emp>> map= list.stream().collect(Collectors.groupingBy(Employee::getStatus)); |
partitioningBy | Map<Boolean, List<T>> | 根據true或false進行分區(qū) | Map<Boolean,List<Emp>>vd= list.stream().collect(Collectors.partitioningBy(Employee::getManage)); |
6. Optional
`Optional<T> 類(java.util.Optional) 是一個容器類,代表一個值存在或不存在,原來用 null 表示一個值不存在,現在 Optional 可以更好的表達這個概念。并且可以避免空指針異常。
-
常用方法
Optional.of(T t) : 創(chuàng)建一個 Optional 實例 Optional.empty() : 創(chuàng)建一個空的 Optional 實例 Optional.ofNullable(T t):若 t 不為 null,創(chuàng)建 Optional 實例,否則創(chuàng)建空實例 isPresent() : 判斷是否包含值 orElse(T t) : 如果調用對象包含值,返回該值,否則返回t orElseGet(Supplier s) :如果調用對象包含值,返回該值,否則返回 s 獲取的值 map(Function f): 如果有值對其處理,并返回處理后的Optional,否則返回 Optional.empty() flatMap(Function mapper):與 map 類似,要求返回值必須是Optional
7. Date Time API
Date Time API (JSR 310)進一步加強對日期與時間的處理
-
為什么要發(fā)布新的 Date Time API ?
舊版Java中,日期時間API存在諸多問題,其中有:
-
非線程安全 -
java.util.Date
是非線程安全的,所有的日期類都是可變的,這是Java日期類最大的問題之一。 -
設計很差 ? Java的日期/時間類的定義并不一致,在
java.util
和java.sql
的包中都有日期類,此外用于格式化和解析的類在java.text
包中定義。java.util.Date
同時包含日期和時間,而java.sql.Date僅包含日期,將其納入java.sql
包并不合理。另外這兩個類都有相同的名字,這本身就是一個非常糟糕的設計。 -
時區(qū)處理麻煩 ? 日期類并不提供國際化,沒有時區(qū)支持,因此
Java
引入了java.util.Calendar
和java.util.TimeZone
類,但他們同樣存在上述所有的問題。
新的
java.time
包涵蓋了所有處理日期,時間,日期/時間,時區(qū),時刻(instants
),過程(during
)與時鐘(clock
)的操作。以下為兩個比較重要的 API:- Local(本地) ? 簡化了日期時間的處理,沒有時區(qū)的問題。
- Zoned(時區(qū)) ? 通過制定的時區(qū)處理日期時間。
-
非線程安全 -
-
LocalDate、LocalTime、LocalDateTime
LocalDate
、LocalTime
、LocalDateTime
類的實例是不可變的對象,分別表示使用 ISO-8601日歷系統(tǒng)的日期、時間、日期和時間。它們提供了簡單的日期或時間,并不包含當前的時間信息。也不包含與時區(qū)相關的信息。public static void main(String[] args) { /** * LocalDate * * getYear() 當前日期年份信息 * getMonth() 當前日期月份信息 * getDayOfMonth() 當前日期是一個月中的第幾天 * getDayOfWeek() 當前日期是周幾 * lengthOfMonth() 當前月有多少天 * isLeapYear 是否是閏年 */ System.out.println("*******************LocalDate*******************"); LocalDate localDate = LocalDate.of(2020, 9, 1); System.out.println(localDate.getYear() + "\t" + localDate.getMonth() + "\t" + localDate.getDayOfMonth() + "\t" + localDate.getDayOfWeek() + "\t" + localDate.lengthOfMonth() + "\t" + localDate.isLeapYear()); LocalDate now = LocalDate.now(); System.out.println(now.get(ChronoField.YEAR) + "\t" + now.get(ChronoField.MONTH_OF_YEAR) + "\t" + now.get(ChronoField.DAY_OF_MONTH)); /** * LocalTime * * getHour 時 * getMinute 分 * getSecond 秒 */ System.out.println("*******************LocalTime*******************"); LocalTime localTime = LocalTime.of(20, 44, 12); System.out.println(localTime.getHour() + "\t" + localTime.getMinute() + "\t" + localTime.getSecond()); /** * 解析字符串 * 默認格式: yyyy-MM-dd */ System.out.println("*******************解析字符串*******************"); LocalDate localDate2 = LocalDate.parse("2020-09-01"); System.out.println(localDate2.toString()); /** * 解析字符串 * 默認格式: HH:mm:ss.SSS */ LocalTime localTime2 = LocalTime.parse("20:42:12.828"); System.out.println(localTime2.toString()); /** * 互相進行類型轉換 */ System.out.println("*******************互相進行類型轉換*******************"); LocalDateTime localDateTime1 = LocalDateTime .of(2020, 9, 1, 16, 12, 10, 888) .atZone(ZoneId.of("Asia/Shanghai")) .toLocalDateTime(); System.out.println(localDateTime1); //LocalDate + LocalTime -> LocalDateTime LocalDateTime localDateTime2 = LocalDateTime.of(localDate2, localTime2); System.out.println(localDateTime2); //組合拼接 LocalDateTime localDateTime3 = localDate2.atTime(10, 10, 10); System.out.println(localDateTime3); LocalDateTime localDateTime4 = localDate2.atTime(localTime2); System.out.println(localDateTime4); LocalDateTime localDateTime5 = localTime2.atDate(localDate2); System.out.println(localDateTime5); LocalDateTime localDateTime6 = LocalDateTime.parse("2020/09/01 16:19:20.888", DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.SSS")); System.out.println(localDateTime6); LocalDate localDate3 = localDateTime6.toLocalDate(); System.out.println(localDate3); LocalTime toLocalTime3 = localDateTime6.toLocalTime(); System.out.println(toLocalTime3); }
******************* LocalDate ******************* 2020 SEPTEMBER 1 TUESDAY 30 true 2020 9 2 ******************* LocalTime ******************* 20 44 12 ******************* 解析字符串 ******************* 2020-09-01 20:42:12.828 ******************* 互相進行類型轉換 ******************* 2020-09-01T16:12:10.000000888 2020-09-01T20:42:12.828 2020-09-01T10:10:10 2020-09-01T20:42:12.828 2020-09-01T20:42:12.828 2020-09-01T16:19:20.888 2020-09-01 16:19:20.888
-
Instatnt
Instant對時間的建模方式是以
UTC
時區(qū)的1970年1月1日午夜時分開始所經歷的秒數進行計算,它不包含時區(qū)信息。public static void main(String[] args) { //獲取當前時間戳 long milli = Instant.now().toEpochMilli(); System.out.println("milli:" + milli); //根據某個時間戳獲取Instant實例 Instant instant = Instant.ofEpochMilli(milli); System.out.println("instant:" + instant); //minusSeconds() 減一秒 Instant instant2 = instant.minusSeconds(1L); System.out.println("instant2:" + instant2); //isBefore()和isAfter()比較大小 System.out.println(instant.isAfter(instant2)); }
-
Duration和Period
Duration
用于計算兩個“時間”間隔,Period
用于計算兩個“日期”間隔public static void main(String[] args) { Duration d1 = Duration.between(LocalDateTime.of(2020, 9, 1, 15, 55, 55, 888), LocalDateTime.now()); Duration d2 = Duration.between(LocalTime.of(17, 55, 10), LocalTime.now()); Duration d3 = Duration.between(Instant.ofEpochMilli(1599037854143L), Instant.now()); System.out.println(d3.toMinutes()); //Duration對象用秒和納秒來衡量時間的長短,所以入參不能使用LocalDate類型, 否則拋UnsupportedTemporalTypeException: Unsupported unit: Seconds //Duration.between(LocalDate.of(2019, 10, 7), LocalDate.now()); //如果想要對多個時間對象進行日期運算,可以用Period Period p1 = Period.between(LocalDate.of(2019, 1, 1), LocalDate.now()); System.out.println(p1.getYears() + "\t" + p1.getMonths() + "\t" + p1.getDays()); }
-
TemporalAdjuster
TemporalAdjuster
時間校正器。有時我們可能需要獲取例如:將日期調整到“下個周日”等操作。TemporalAdjusters
: 該類通過靜態(tài)方法提供了大量的常用 TemporalAdjuster 的實現。public static void main(String[] args) { LocalDateTime localDateTime = LocalDateTime.now(); // 本年本月最后一天 System.out.println(localDateTime.with(TemporalAdjusters.lastDayOfMonth())); // 本年本月第一天 System.out.println(localDateTime.with(TemporalAdjusters.firstDayOfMonth())); // 本年下一月第一天 System.out.println(localDateTime.with(TemporalAdjusters.firstDayOfNextMonth())); // 下一年第一天 System.out.println(localDateTime.with(TemporalAdjusters.firstDayOfNextYear())); // 本年最后一天 System.out.println(localDateTime.with(TemporalAdjusters.lastDayOfYear())); // 下一個周五 System.out.println(localDateTime.with(TemporalAdjusters.next(DayOfWeek.FRIDAY))); // 本月第一個周五 System.out.println(localDateTime.with(TemporalAdjusters.firstInMonth(DayOfWeek.FRIDAY))); // 本月最后一個周五 System.out.println(localDateTime.with(TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY))); // 下一個周五,如果當前是周五則返回當前時間 System.out.println(localDateTime.with(TemporalAdjusters.nextOrSame(DayOfWeek.FRIDAY))); // 前一個周五 System.out.println(localDateTime.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY))); // 前一個周五,如果當前是周五則返回當前時間 System.out.println(localDateTime.with(TemporalAdjusters.previousOrSame(DayOfWeek.FRIDAY))); }
2020-09-30T17:45:14.274 2020-09-01T17:45:14.274 2020-10-01T17:45:14.274 2021-01-01T17:45:14.274 2020-12-31T17:45:14.274 2020-09-04T17:45:14.274 2020-09-04T17:45:14.274 2020-09-25T17:45:14.274 2020-09-04T17:45:14.274 2020-08-28T17:45:14.274 2020-08-28T17:45:14.274
-
DateTimeFormatter
java.time.format.DateTimeFormatter
類,該類提供了三種格式化方法:預定義的標準格式
語言環(huán)境相關的格式
自定義的格式
public static void main(String[] args) { //日期轉字符串 LocalDate ld = LocalDate.of(2020, 9, 1); String s1 = ld.format(DateTimeFormatter.BASIC_ISO_DATE); System.out.println(s1); String s2 = ld.format(DateTimeFormatter.ISO_LOCAL_DATE); System.out.println(s2); //字符串轉日期 LocalDateTime ld1 = LocalDateTime.parse("2020-09-01 18:00:00.888", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")); System.out.println(ld1); }
20200901 2020-09-01 2020-09-01T18:00:00.888
-
時區(qū)的處理
Java8
中加入了對時區(qū)的支持,帶時區(qū)的時間為分別為:ZonedDate
、ZonedTime
、ZonedDateTime
其中每個時區(qū)都對應著 ID,地區(qū)ID都為 “{區(qū)域}/{城市}”的格式
public static void main(String[] args) { //獲取所有合法的“區(qū)域/城市”字符串 Set<String> availableZoneIds = ZoneId.getAvailableZoneIds(); //availableZoneIds.forEach(System.out::println); //獲取系統(tǒng)默認時區(qū) ZoneId systemZoneId = ZoneId.systemDefault(); System.out.println("當期時區(qū): " + systemZoneId); // 獲取當前時間日期 ZonedDateTime date1 = ZonedDateTime.parse("2015-12-03T10:15:30+05:30[Asia/Shanghai]"); System.out.println("date1: " + date1); //創(chuàng)建時區(qū) ZoneId id = ZoneId.of("Europe/Paris"); System.out.println("ZoneId: " + id); //LocalDate、LocalDateTime、Instant 轉 ZonedDateTime ZonedDateTime zdt1 = LocalDate.of(2020, 9, 3).atStartOfDay(ZoneId.systemDefault()); ZonedDateTime zdt2 = LocalDateTime.of(2020, 9, 3, 14, 10, 55, 888) .atZone(ZoneId.of("Asia/Shanghai")); ZonedDateTime zdt3 = Instant.now().atZone(ZoneId.of("Asia/Yerevan")); //Instant轉LocalDateTime LocalDateTime ldt1 = LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault()); }