《java8 in action》读书笔记
语言需要不断改进以跟进硬件的更新或满足程序员的期待。所以java也需要演变,不然就会不适应IT产业的发展从而被别的语言淘汰,java语言现在更新到java12,不过业界主流应该还是java7或java8。
Java8中的主要变化反映了它开始远离常侧重改变现有值的经典面向对象思想,而向函数式编程领域转变。在大面上考虑做什么被认为是头等大事,并和如何实现区分开来。
行为参数化
简单讲就是把代码通过参数传递给方法
- Lambda:直接传方法中的代码。
- 方法引用:把方法名作为参数传递,其实传递的是方法中的代码,比lambda可读性好。
Lambda
没有名称,但有参数列表、函数主体、返回类型,通过一个特殊的箭头(->)把参数列表和函数主题分开。
lambda可以赋给函数式接口,函数式接口就是只定义了一个抽象方法的接口,lambda就是这个抽象方法的实现。
lambda中可以没有限制的捕获实例变量和静态变量,但局部变量只能捕获一次。而且要把局部变量声明成final或者当final变量使用。原因是实例变量存储在堆上,局部变量存在于栈上,而lambda是在一个线程中使用的,lambda线程可能会在分配该局部变量的线程把变量收回之后访问它。因此,Java在访问局部变量时,实际上是在访问它 的副本,而不是访问原始变量,如果该变量只赋值一次,副本和原始值没区别的。
任何lambda都不允许抛出受检异常,如果需要lambda表达式来抛出异常,可以定义一个自己的函数式接口或者把lambda放到try/catch中。
方法引用
方法引用的类类别:
1. 指向静态方法的方法引用, 比如Integer::parseInt。
2. 指向任意类型实例方法的方法引用,引用一个对象的方法,这个对象本身是lambda的一个参数。比如(String s) -> s.toUppeCase(),可以写成 String::toUppeCase
3. 指向现有对象的实例方法的方法引用。在lambda的函数体中调用一个已存在对象的方法,对象名::方法名。
流处理
流就是从支持数据处理操作的源生成的元素序列,流处理的出现顺应了多核处理机的新时代,方便java进行并行处理。java的Synchronized在多核CPU上的执行成本比想象中的要高,多核处理器的每个内核都有独立的高速缓存,加锁需要这些高速缓存同步运行,然而这又需要在内核间进行较慢的缓存一致性协议通信。可以把流看作一个天然可并行的迭代器,同时加了类似linux管道操作的能力,可以一次迭代对集合中的数据进行多种有序处理,而且处理是可以利用多个cpu的并行处理,而且实现起来非常透明,几乎免费,比搞Thread更简单。流处理同时也是延后执行的,跟spark的迭代设计一致,都是由collection,reduce等action算子统一触发之前的操作定义。
流处理的并行能力相应的也产生了一些限制:代码无法访问共享的可变数据。通过参数传给流的方法都是无状态函数或者叫纯函数,不会改变某个共享的局部或全局对象。
收集器
收集器是为了收集流中的数据,对流做最终的归约操作。流本身提供了collect接口,可以通过java.util.stream.collectors中的各种静态工厂方法把流归约成需要的数据结构。
默认方法
是为了让库设计师可以更容易的改进接口。简而言之,在接口中定义一个方法同时在接口中实现,不用接口的继承类做任何改变。如若不然,新增一个接口,已存在的类都得去做实现,会让人疯掉。
Optional<T>
避免NPE,变量存在时Optional类只是对类进行简单的封装,变量不存在时返回一个空的Optional实例。空的Optional实例是一个真实存在的对象,避免返回Null。
在写程序的过程中常用的一个Optional语法糖是提取对象中的某属性的值,不用先判空,特别是在遇到层次很深的属性,非常方便。
person.flatMap(Person::getCar) .flatMap(Car::getInsurance) .map(Insurance::getName) .orElse("Unknown");
用Optional声明的对象无法实现序列化,这个是java8设计的时候没让Optional类继承Serializable接口。
CompletableFuture
CompleteableFuture是对Future异步编程对升级,采用类似流处理的方式,把业务实现,通常是IO请求,如何响应IO请求结果流式的声明出来,并通过静态的接口进行触发。
public Stream<CompletableFuture<String>> findPricesStream(String product) {
return shops.stream() .map(shop -> CompletableFuture.supplyAsync(() -> shop.getPrice(product), executor)) .map(future -> future.thenApply(Quote::parse)) .map(future -> future.thenCompose(quote -> CompletableFuture.supplyAsync(() -> Discount.applyDiscount(quote), executor))); } CompletableFuture[] futures = findPricesStream("myPhone") .map(f -> f.thenAccept(System.out::println)) .toArray(size -> new CompletableFuture[size]); CompletableFuture.allOf(futures).join();
java8的日期和时间API
本意是为了简化Java8之前Date和CalendarAPI的混乱,可是引入了新的LocalDate, LocalTime, LocalDateTime感觉更混乱了,并且LocalDate不能很方便的转成Date, 对兼容老代码并不友好。