前言
在这篇文章中,主要是介绍了一下在Java8中的stream、Date API、Annotation、CompletableFuture几个特性。
介绍
Stream
stream流可以说是我认为Java8上面最重要的一个特性之一。当初还没接触Java8的时候,最先接触的是RxJava,而在RxJava中最重要的思想就是将任务都转换为流,从而实现链式编程。而在Java8中,steam流就是将一组元素转化为流式操作,而这是我们现在所推崇的。
A sequence of elements supporting sequential and parallel aggregate operations.
这是Java8中stream的声明,我的理解是:
- Stream是元素的集合,实际上就是类似于Iterator的操作;
- 可以支持顺序和并行的对原Stream进行汇聚的操作;
图片就是对于Stream例子的一个解析,可以很清楚的看见:原本一条语句被三种颜色的框分割成了三个部分。红色框中的语句是一个Stream的生命开始的地方,负责创建一个Stream实例;绿色框中的语句是赋予Stream灵魂的地方,把一个Stream转换成另外一个Stream,红框的语句生成的是一个包含所有nums变量的Stream,进过绿框的filter方法以后,重新生成了一个过滤掉原nums列表所有null以后的Stream;蓝色框中的语句是丰收的地方,把Stream的里面包含的内容按照某种算法来汇聚成一个值,例子中是获取Stream中包含的元素个数。
可以看到,其实stream中有很多函数和RxJava中的操作函数都是十分相似的,所以如果熟悉其中的任意一个,都能够很快掌握另一个。
现在介绍一下常用的几个函数:
Filter过滤
过滤通过一个predicate接口来过滤并只保留符合条件的元素,该操作属于中间操作,所以我们可以在过滤后的结果来应用其他Stream操作(比如forEach)。forEach需要一个函数来对过滤后的元素依次执行。forEach是一个最终操作,所以我们不能在forEach之后来执行其他Stream操作。
stringCollection
.stream()
.filter((s) -> s.startsWith("a"))
.forEach(System.out::println);
Sort 排序
排序是一个中间操作,返回的是排序好后的Stream。如果你不指定一个自定义的Comparator则会使用默认排序。
stringCollection
.stream()
.sorted()
.filter((s) -> s.startsWith("a"))
.forEach(System.out::println);
Map 映射
中间操作map会将元素根据指定的Function接口来依次将元素转成另外的对象,下面的示例展示了将字符串转换为大写字符串。你也可以通过map来讲对象转换成其他类型,map返回的Stream类型是根据你map传递进去的函数的返回值决定的。
stringCollection
.stream()
.map(String::toUpperCase)
.sorted((a, b) -> b.compareTo(a))
.forEach(System.out::println);
可以看出,整个操作都是流式操作,和RxJava很相似,剩下常用的函数就不一一介绍了,感兴趣的童鞋可以参考stream语法详解。
CompletableFuture
CompletableFuture类实现了CompletionStage和Future接口。Future是Java 5添加的类,用来描述一个异步计算的结果,但是获取一个结果时方法较少,要么通过轮询isDone,确认完成后,调用get()获取值,要么调用get()设置一个超时时间。但是这个get()方法会阻塞住调用线程,这种阻塞的方式显然和我们的异步编程的初衷相违背。
为了解决这个问题,JDK吸收了guava的设计思想,加入了Future的诸多扩展功能形成了CompletableFuture。
在Android编程中,可以使用CompletableFuture来配合Executors来替代旧版的future类实现,但是CompletableFuture所需要在Android N以上才能使用,需要声明
@RequiresApi(api = Build.VERSION_CODES.N)
String result = CompletableFuture.supplyAsync(() -> "hello").thenApply(s -> s + " world").join();
可以看出其实CompletableFuture也是支持流式操作的,有许多常用的操作函数,其中以Async结尾的都是支持异步处理的,并且可以自己指定线程池,也可以使用默认的线程池。
这里只是简单介绍一下这个类,使用这个类可以更更加优雅的实现异步任务的处理,更详细的可以参考Java CompletableFuture 详解
Date API
Java 8新的Date-Time API (JSR 310)受Joda-Time的影响,提供了新的java.time包,可以用来替代 java.util.Date和java.util.Calendar。一般会用到Clock、LocaleDate、LocalTime、LocaleDateTime、ZonedDateTime、Duration这些类,对于时间日期的改进还是非常不错的。
Clock 时钟
Clock类提供了访问当前日期和时间的方法,Clock是时区敏感的,可以用来取代 System.currentTimeMillis() 来获取当前的微秒数。某一个特定的时间点也可以使用Instant类来表示,Instant类也可以用来创建老的java.util.Date对象。
Timezones 时区
在新API中时区使用ZoneId来表示。时区可以很方便的使用静态方法of来获取到。 时区定义了到UTS时间的时间差,在Instant时间点对象到本地日期对象之间转换的时候是极其重要的。
LocalTime 本地时间
LocalTime 定义了一个没有时区信息的时间,例如 晚上10点,或者 17:30:15。下面的例子使用前面代码创建的时区创建了两个本地时间。之后比较时间并以小时和分钟为单位计算两个时间的时间差.
LocalDate 本地日期
LocalDate 表示了一个确切的日期,比如 2014-03-11。该对象值是不可变的,用起来和LocalTime基本一致。下面的例子展示了如何给Date对象加减天/月/年。另外要注意的是这些对象是不可变的,操作返回的总是一个新实例。
LocalDateTime 本地日期时间
LocalDateTime 同时表示了时间和日期,相当于前两节内容合并到一个对象上了。LocalDateTime和LocalTime还有LocalDate一样,都是不可变的。LocalDateTime提供了一些能访问具体字段的方法。
Annotation
在Java 5中使用注解有一个限制,即相同的注解在同一位置只能声明一次。Java 8引入重复注解,这样相同的注解在同一地方也可以声明多次。重复注解机制本身需要用@Repeatable注解。Java 8在编译器层做了优化,相同注解会以集合的方式保存,因此底层的原理并没有变化。
多重注解的意思大概如下:
@interface Hints {
Hint[] value();
}
@Repeatable(Hints.class)
@interface Hint {
String value();
}
//旧版声明方法
@Hints({@Hint("hint1"), @Hint("hint2")})
class Person {}
//新版声明方法
@Hint("hint1")
@Hint("hint2")
class Person {}
即便我们没有在Person类上定义@Hints注解,我们还是可以通过 getAnnotation(Hints.class) 来获取 @Hints注解,更加方便的方法是使用 getAnnotationsByType 可以直接获取到所有的@Hint注解。
小结
这篇文章只是浅显地介绍了Java8中的一些属性,当然还有很多其他的属性可以慢慢地区了解使用。其中写文章过程中也参考了不少资料,感兴趣的可以参考应用链接。