关于剩余Java8新特性知识点总结,包含:默认方法、Optional、CompletableFuture、时间相关。
默认方法皆在帮助Java新功能能够兼容低版本JDK已开发的程序。 好比说,给一个低版本已存在的接口增长新方法,那原来实现该接口的类是否是都须要实现新的方法,这很是不友好,也不利于项目JDK版本的升级,因此引入新的规则默认方法。java
使用方法也很是简单,只须要在接口方法前加上关键字default
,而后再将其实现之就行了。json
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应运而生。spa
Optional就是将用户建立的对象再进一步封装,使用户对象只是Optional对象的一部分,这样对于开发人员层面来讲,就不会直接操做未定义的对象了。设计
假设咱们的用户类就是上一节中的Son。建立一个包含Son对象的Optional,而且Son对象目前指向为null。使用静态方法Optional.empty()
。3d
Optional<Son> optCar = Optional.empty();
接着将存在的用户对象Son放到Optional中。代理
Son son = new Son(); Optional<Son> sonOptional = Optional.of(son);
不过使用Optional.of
须要保证参数必须不为null,不然就会抛出NullPointerException
异常,为了解决这个问题,可使用下面这个方法。code
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()参数是字符串(大洲/城市),没有枚举类,须要开发人员手动输入。
个人公众号用于博客同步。