關於剩餘Java8新特性知識點總結,包含:默認方法、Optional、CompletableFuture、時間相關。java
默認方法皆在幫助Java新功能能夠兼容低版本JDK已開發的程序。 好比說,給一個低版本已存在的接口增長新方法,那原來實現該接口的類是否是都須要實現新的方法,這很是不友好,也不利於項目JDK版本的升級,因此引入新的規則默認方法。json
使用方法也很是簡單,只須要在接口方法前加上關鍵字default
,而後再將其實現之就行了。bash
public interface Parent {
default void hello(){
System.out.println("hello");
}
}
public class Son implements Parent {
public static void main(String[] args) {
Son son = new Son();
son.hello();
}
}
複製代碼
默認方法很好的解決了菱形繼承
的問題,雖然Java是單繼承,通常狀況下不會出現這種問題,可是別忘了Java除了繼承,還有實現,假設有一下這種狀況。工具
Son實現了Parent、Parent1,Parent和Parent1都繼承了GrandParent,而GrandParent中有一個抽象方法hello,而後Son就會報錯。而若是將GrandParent中的方法改爲默認方法,這個爲你就解決了。post
//這種方式,Son會報錯
public interface GrandParent {
void hello();
}
//這種方式就ok了
public interface GrandParent {
default void hello(){
System.out.println("hello");
}
}
複製代碼
其實有了默認方法,一個Java類能夠實現N多個接口,不管它們多麼錯綜複雜。可是爲了代碼層次,最好使用代理模式
,提早實現一層代理類封裝好全部的接口,真正繼承的類再去繼承代理類,這樣使用起來就會很舒服了。性能
但凡寫過Java的同窗,必定知道null
的存在,null是那些從未定義對象的指向。爲了不NullPointerException
的出現,代碼中要加大量的if
判斷。爲了讓這種冗餘的無心義的代碼消失,爲了更加符合Java設計哲學,Optional應運而生。ui
Optional就是將用戶建立的對象再進一步封裝,使用戶對象只是Optional對象的一部分,這樣對於開發人員層面來講,就不會直接操做未定義的對象了。spa
假設咱們的用戶類就是上一節中的Son。建立一個包含Son對象的Optional,而且Son對象目前指向爲null。使用靜態方法Optional.empty()
。設計
Optional<Son> optCar = Optional.empty();
複製代碼
接着將存在的用戶對象Son放到Optional中。代理
Son son = new Son();
Optional<Son> sonOptional = Optional.of(son);
複製代碼
不過使用Optional.of
須要保證參數必須不爲null,不然就會拋出NullPointerException
異常,爲了解決這個問題,可使用下面這個方法。
Optional<Son> sonOptional = Optional.ofNullable(son);
複製代碼
不只僅是判空問題,Optional能夠自然的和Stream結合。爲了方便,咱們給Son類增長屬性name、age。
@Data
public class Son implements Parent,Parent2 {
private String name;
private Integer age;
}
複製代碼
如今假設咱們須要知道son的name,首先須要判斷son是否爲空,再使用方法son.getName()對吧。然而有了Optional咱們可使用map這麼寫。
//原始寫法
Son son = null;
if(Objects.nonNull()){
String name = son.getName();
}
//新寫法
Son son = null;
Optional<Son> sonOptional = Optional.of(son);
Optional<String> name = sonOptional.map(Son::getName);
複製代碼
值得一提的是經過Stream操做轉化來的對象仍是Optional,只不過泛型變成了預期值類型。
若是看過我上篇寫的Stream的博客,那麼除了map
,你必定還記得flatMap
, flatMap 能夠合併Stream,一樣的 flatMap 能夠將多個嵌套的Optional進行合併。下面舉個例子說明一下,爲了說明問題新建兩個類Computer、CPU,顯而易見CPU是Computer的一部分。咱們須要知道某品牌電腦CPU的生產廠家。
@Data
public class CPU {
private String name;
private String manufacturers;
}
@Data
public class Computer {
private String name;
private Optional<CPU> cpu;
}
複製代碼
首先使用map看一下效果。
Optional<Computer> optionalComputer = Optional.of(computer);
Optional<String> stringOptional = optionalComputer
.map(Computer::getCpu)
.map(CPU::getManufacturers);//別說運行了,這行編譯失敗
System.out.println(stringOptional.get());
複製代碼
第4行編譯失敗,那是由於經歷第3行,此時對象的結構爲Optional<Optional<CPU>>
,此時的泛型是Optional<CPU>
,沒法直接調用CPU::getManufacturers
。
因此就須要使用flatMap
將兩層Optional合併。
Optional<Computer> optionalComputer = Optional.of(computer);
Optional<String> stringOptional = optionalComputer
.flatMap(Computer::getCpu)
.map(CPU::getManufacturers);
複製代碼
ok,完美執行了。固然了,Stream中有不少方法這裏都適用,好比filter。
最直接的方法就是get()
,可是get()方法須要提早判空,若是用戶對象(就是泛型類的對象)爲null,get()方法就會報錯。
stringOptional.get()
複製代碼
固然,你要執意使用get()方法,Java也提供了更友好的方法orElse()
,這個方法能夠當對象是空的時候,給一個默認值。
stringOptional.orElse("juejin")
複製代碼
對於orElse(),還有一個升級版本的方法orElseGet()
,是orElse()延時版本,當只有使用默認值的時候,纔會運算orElseGet()
的參數,這樣就避免了默認值是耗時操做影響性能。
stringOptional.orElseGet(()->"juejin")
複製代碼
若是你想在對象不存在的時候,拋出一個異常,就可使用方法orElseThrow()
。
stringOptional.orElseThrow(Exception::new)
複製代碼
還有一個很是好用的方法ifPresent()
,這個方法能夠判斷對象是否存在。
if (stringOptional.isPresent()){
System.out.println("stringOptional.isPresent()");
}
複製代碼
雖然說如今有不少成型的第三方工具包,例如Hutool。處理時間問題多了不少選擇,可是JDK自己的方法,不只提供了某種意義上的最佳實踐,還讓代碼侵入性幾乎爲零。
相信不少使用過Java8的小夥伴都用過LocalDateTime
,沒錯,就是LocalDate和LocalTime 的結合形式,分別控制着年月日、時分秒。
LocalDate localDate = LocalDate.of(2019,11,24);
localDate.getYear();
localDate.getMonth();
localDate.getDayOfYear();
LocalTime localTime = LocalTime.of(18,40,03);
localTime.getHour();
localTime.getMinute();
localTime.getSecond();
複製代碼
這兩個方法比較簡單,能夠靈活的對年月日
、時分秒
進行讀取和操做。然而真正使用多的是他們的結合版本LocalDateTime
。
這個類能夠獲取機器時間,就是從1970年開始計算的時間,舉例獲取時間戳。
Instant instant = Instant.now();
System.out.println(instant.getEpochSecond());
複製代碼
固然getEpochSecond()這個方法還能夠傳參數,相對於當前時間位移必定的時間間隔。
Duration
能夠獲取兩個時間之間的間隔。
Duration d1 = Duration.between(time1, time2);
Duration d1 = Duration.between(dateTime1, dateTime2);
Duration d2 = Duration.between(instant1, instant2);
複製代碼
其中參數能夠是 LocalTimes 、 LocalDateTimes 、 Instant。以上能夠計算秒和納秒之間的大小。
若是須要計算年、月、日之間的時間間隔,就須要用Period
。
Period tenDays = Period.between(LocalDate.of(2014, 3, 8),
LocalDate.of(2014, 3, 18));
複製代碼
就拿LocalDateTimes舉例吧,這個用的最多,新的時間類型能夠快捷的對時間進行修改。
LocalDateTime localDateTime = LocalDateTime.now();
//直接修改時間,不對原始數據操做,生成新的對象。
localDateTime.withHour(1).withMonth(1);
//經過位移修改時間,不對原始數據操做,生成新的對象。
localDateTime.plusDays(12);
localDateTime.plusMinutes(33);
複製代碼
除了顯示的修改時間,還能夠用JDK提供的一些提早定義好的方法。
import static java.time.temporal.TemporalAdjusters.*;//注意須要這麼導入
LocalDate date1 = LocalDate.of(2019, 11, 24);
System.out.println(date1);//2019-11-24
LocalDate date2 = date1.with(nextOrSame(DayOfWeek.SUNDAY));
System.out.println(date2);//2019-11-24
LocalDate date3 = date2.with(lastDayOfMonth());
System.out.println(date3);//2019-11-30
複製代碼
更多操做見下表
這個沒必要多說,將事件類型轉化成String類型時,須要的樣式格式。
LocalDateTime localDateTime = LocalDateTime.now();
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:SS", Locale.CHINA);
System.out.println(localDateTime.format(dateTimeFormatter));
//2019-11-24 19:41:77
複製代碼
處理不一樣的時區,ZoneId 類替代了原來的 TimeZone 類。
LocalDateTime localDateTime = LocalDateTime.now();
ZoneId romeZone = ZoneId.of("Europe/Rome");
ZoneId shangHaiZone = ZoneId.of("Asia/Shanghai");
System.out.println(localDateTime.atZone(romeZone));
System.out.println(localDateTime.atZone(shangHaiZone));
//2019-11-24T19:52:13.105+01:00[Europe/Rome]
//2019-11-24T19:52:13.105+08:00[Asia/Shanghai]
複製代碼
惟一不爽的是ZoneId.of()參數是字符串(大洲/城市),沒有枚舉類,須要開發人員手動輸入。
個人公衆號用於博客同步。