#1. 引入更多编程概念、编程工具,简洁、灵活地编写代码。
优化步骤:
1)进一步复用代码,抽取公共的逻辑代码,参数化可变的逻辑代码 2)声明式编程 3)并发编程,隐藏底层实现 ##1.1 lambda表达式 进一步复用逻辑代码,行为参数化,引入思想。###lambda表达式 相较于之前的匿名类实现行为参数化,更简洁 。有参数列表、箭头、函数主体、返回类型、异常列表。
(parameters)->expression(parameters)->{statements;}
###函数式接口
1)函数式接口:只定义一个抽象方法的接口,通常会使用@FunctionalInterface注解一下; 2)lambda表达式可以当作函数式接口的实现实例; 3)常用的函数式接口:在java.util.function包预定义了一些常用函数式接口:Predicate{ boolean test(T t); }Consumer { void accept(T t); }Function { R apply(T t); }Supplier { T get(); }UnaryOperate extends Function {}BinaryOperator extends BiFunction {}BiPredicate { boolean test(T t, U u); }BiConsumer { void accept(T t, U u); }BiFunction { R apply(T t, U u); }
4)原型类型特化的函数式接口:避免装箱、拆箱操作,输入参数或返回类型是原始类型;
###类型坚持、类型推断以及限制
1)lambda类型检查:lambda的类型是从使用lambda的上下文推断出来的,目标类型的抽象方法签名(函数描述符)可以兼容lambda表达式的签名。另外如果一个lambda的主体是一个语句表达式,它就和一个返回void的函数描述符兼容。 2)lambda类型推断以:既然可以推断出lambda的签名,就可以知道lambda表达式的参数类型,省去lambda中参数类型的书写。 3)限制:使用自由变量,即外层作用域中定义的变量,必须声明称final###方法引用 方法引用:可以被看作仅仅调用特定方法的lambda的一种快捷写法,可以重复使用现有的方法来创建lambda表达式。有:
1)指向静态方法的方法引用; 2)指向任意类型实例方法的方法引用; 3)指向现有对象的实例方法的方法引用; 4)构造函数引用###复合lambda高阶函数
##1.2 流Stream 很多业务逻辑都涉及类似数据库的集合操作,都会使用到Collection。Collection主要是为了存储和访问数据,Stream主要用于描述对数据集合Collection的计算处理(声明式处理数据集合,可以透明地并行处理)。
流:从支持数据处理操作的源生成的元素序列。 特点:1)按需计算,可以是无界的,延迟;2)只能遍历一次;3)内部迭代。
使用流:1)一个数据源来执行一个查询;2)一个中间操作链,形成一个流的流水线;3)一个终端操作,只想流水线,并能生成结果。
默认方法: java8在加入Stream时,在原来的集合系统上使用了“默认方法”得以扩展Stream功能而不用每个实现类都实现相关接口方法。
###构建流 原始类型特化流:IntStream, DoubleStream, LongStream避免装箱成本。1)每个接口都带来性的常用的数值归约方法。 2)Stream提供mapToXXX转成特化流,特化流通过boxed转回对象流。3)提供range和rangeClosed生成流。
构建方法:1)由值创建:Stream.of();2)由数组构建:Arrays.Stream();3)由文件生成:Files.line();4)由函数生成:Stream.iterate, Stream.generate
###使用流 Stream接口的方法
1)筛选和切片:filter, distinct, limit, skip ; 2)映射:map, flatMap(把一个流中的每个值都转成另一个流,然后把所有的流连接起来成为一个流); 3)查找和匹配:allMatch, anyMatch, noneMatch, findFirst, findAny; 4)归约折叠:reduce,可以支持并行计算,collect;###用流收集数据 1)collect是一个终端操作,参数是将流中元素累计到汇总结果的各种方式(收集器)
1)预定义收集器:Collector类提供的工厂方法创建的收集器:3大功能:将流元素归约和汇总为一个值;元素分组groupingBy;元素分区partitioningBy。收集器可以复合起来进行多级分组、分区和归约。 2)自定义收集器:public interface Collector{ Supplier supplier(); BiConsumer accumulator(); BinaryOperator combiner(); Function finisher(); Set characteristics(); public static supplier, BiConsumer accumulator, BinaryOperator combiner, Function finisher, Characteristics... characteristics) {...} }Collector of(Supplier supplier, BiConsumer accumulator, BinaryOperator combiner, Characteristics... characteristics) {... } public static Collector of(Supplier
###并行流 底层使用分支/合并框架处理并行流,用递归方式将可以并行的任务拆分成更小的任务,在不同的线程上执行,然后将各个子任务的结果合并起来生成整体结果。
高效使用并行流
1)使用适当的基准来检查并行流性能; 2)留意装箱。自动装箱和拆箱会大大减低性能; 3)有些操作本身在并行流上的性能就比顺序流差; 4)流的操作流水线的总计算成本; 5)对于较小的数据量,选择并行流几乎从来都不是一个好的决定; 6)要考虑背后的数据结构是否易于分解; 7)流自身的特定,以及流水线中的中间操作修改流的方式,都可能会改变分解过程的性能; 8)考虑终端操作中合并步骤的代价是大是小;Spliterator Java8的新接口,定义并行流如何拆分它要遍历的数据。
public interface Spliterator{ boolean tryAdvance(Consumer action); Spliterator trySplit(); long estimateSize(); int characteristics();}
##1.3 其他 现代程序设计方法,改善代码质量 ###重构、测试、调试 改善代码的可读性:
1)用Lambda表达式取代匿名类:在匿名类中this代表类自身,Lambda里this代表包含类;匿名类可以屏蔽包含类的同名变量,而Lambda表达式不能; 2)用方法引用重构Lambda表达式; 3)用Stream API重构命令式的数据处理;增加代码的灵活度: 1)采用函数接口;
2)有条件的延迟执行; 3)环绕执行;Lambda表达式会使栈跟踪的分析变得更为复杂。流提供peek方法在分析Stream流水线时,可以将中间变量的值输出到日志中,常用于调试代码。
###default默认方法 Java8的接口可以通过默认方法和静态方法提供方法的代码实现。通过默认方法可以指定接口方法的默认实现,实现类如果不显示提供该方法的具体实现,就会自动继承默认的实现。这种机制可以平滑地进行接口的优化和演进。默认方法的开头以关键字default修饰,方法体与常规的类方法相同。
静态方法及接口同时定义接口以及工具辅助类是Java语言常用的一种模式,工具类定义了接口实例协作的很多静态方法。在有了默认方法后,这些辅助类就没有存在的必要了,可以把这些辅助类的静态方法转移到接口内部。
java8中抽象类和抽象接口:1)一个类只能继承一个抽象类,但是一个类可以实现多个接口;2)一个抽象类可要通过实例变量保存一个通用状态,而接口是不能有实例变量的。
默认方法的使用模式:1)可选方法;2)行为多继承:类型的多继承,利用正交方法的精简接口,组合接口
解决冲突的规则 1)类中的方法优先级最高。类或父类中声明的方法的优先级高于任何声明默认方法的优先级;
2)如果无法依据第一条进行判断,那么子接口的优先级更高:函数签名相同时,优先级选择拥有最具体实现的默认方法的接口,即如果B继承了A,那么B就比A更具体; 3)如果还是无法判断,继承了多个接口的泪必须通过显示覆盖和调用期望的方法,显示地选择使用哪一个默认方法的实现(x.super.m(...))。 C++中的菱形问题更复杂:因为是类多继承,除了方法的冲突还要考虑到变量的冲突。###Optional 1)java8中引入了一个新的类java.util.Optional<T>对存在或缺失的变量值进行建模;
2)可以使用静态工厂方法Optional.empty,Optional.of以及Optional.ofNullable创建Optional对象。 3)Optional类支持多种方法:比如map, flatMap, filter,在概念上和Stream类相似; 4)null判断,不用isPresent:public static final Optionalmax(Optional i, Optional j) { return i.flatMap(a -> j.map(b -> Math.max(a, b)));}public static int readDurationWithOptional(Properties props, String name) { return ofNullable(props.getProperty(name)) .flatMap(ReadPositiveIntParam::s2i) .filter(i -> i > 0).orElse(0);}
5)orElseGet(Supplier<? extends T> other)是orElse方法的延迟调用版,Supplier方法只有在Optional对象不含只时才调用。如果创建默认值是件耗时费力的工作,可以考虑采用这种方式;
6)Optinal无法序列化###组合式异步编程CompletableFuture 执行比较耗时的操作时,尤其是那些依赖一个或多个远程服务的操作,使用异步任务可以改善程序的性能,加快程序的响应速度。
对集合进行并行计算有2种方式:1)将其转化为并行流,利用map操作开展工作;2)枚举出集合众每一个元素,创建新的线程,在CompletableFurture内对其进行操作。后者提供了更多地灵活性,可以调整线程池的大小,进而确保整体的计算不会因为现场都在等待I/O而发生阻塞。
1)如果进行的实计算密集型的操作,并且没有I/O,那么就推荐使用Stream接口,因为实现简单,同时效率也可能是最高的(如果所有的线程都是计算密集型的,那就没有必要创建比处理器核数更多的线程);
2)如果并行的工作单元还涉及等待I/O的操作,那么使用CompletaleFuture灵活性更好,可以根据Nthreads=Ncpu*Ucpu*(1+W/C)(Ncpu是处理器的核数,Ucpu是期望的CPU利用率,W/C是等待时间与计算时间的比率)设定需要使用的线程数。这种情况不使用并行流的另一个原因:处理刘的流水线中如果发生I/O等待,流的延迟特性会让我们很难判断到底是什么时候触发了等待。###新Date和TIme接口
TemporalField, ChronoField Temporal:定义如何读取和操纵为时间建模的对象的值;LocalDate:不可变对象,提供简单的日期,不含时间和时区信息
LocalTime: LocalDateTIme: Instant:便于机器使用,从1970-01-01开始经历的秒数间隔:
Duration:2个Temporal对象之间的duration Period:以年、月、日的方式对多个时间单位建模 TemporalAdjuster:格式化:
DateFormatter:时区:
ZoneId:区时信息 ZoneDateTime: ZoneOffset: OffsetDateTime:日历系统:
Chronology: ChronoLocalDate: ThaiBuddhistDate: MiguoDate: JapaneseDate: HijrahDate: