高效java8编程

1.重构、测试和调试
1.1.改善可读性和灵活性重构代码
三种简单的重构,利用Lambda表达式、方法引用以及Stream改善程序代码的可读性:
◆重构代码,用Lambda表达式取代匿名类
◆用方法引用重构Lambda表达式
◆用Stream API重构命令式的数据处理
匿名类和为Lambda表达式的区别
◆匿名类和Lambda表达式中的this和super的含义不同。在匿名类中, this代表的是类自身,但是在Lambda中,它代表的是包含类;
◆匿名类可以屏蔽包含类的变量,而Lambda表达式不能(它们会导致编译错误);
◆在涉及重载的上下文里,将匿名类转换为Lambda表达式可能导致最终的代码更加晦涩。
用方法引用重构Lambda表达式
◆为了改善代码的可读性,尽量使用方法引用;
◆应该尽量考虑使用静态辅助方法,比如comparing、 maxBy。这些方法设计之初就考虑了会结合方法引用一起使用;
◆通用的归约操作,比如sum、 maximum,都有内建的辅助方法可以和方法引用结合使用;
用Stream API重构命令式的数据处理
◆建议将所有使用迭代器的数据处理模式处理集合的代码都转换成Stream API的方式;
增加代码的灵活性
◆采用函数接口;
◆有条件的延迟执行;
◆环绕执行;
1.2.使用 Lambda 重构面向对象的设计模式
A.策略模式
策略模式代表了解决一类算法的通用解决方案,可以在运行时选择使用哪种方案。
策略模式包含三部分内容:
◆一个代表某个算法的接口(它是策略模式的接口);
◆一个或多个该接口的具体实现,它们代表了算法的多种实现;
◆一个或多个使用策略对象的客户;
         
B.模板方法
模板方法模式在你“希望使用这个算法,但是需要对其中的某些行进行改进,才能达到希望的效果”时是非常有用的。
C.观察者模式
观察者模式是一种比较常见的方案,某些事件发生时(比如状态转变),如果一个对象(通常我们称之为主题)需要自动地通知其他多个对象(称为观察者),就会采用该方案。
         
D.责任链模式
责任链模式是一种创建处理对象序列(比如操作序列)的通用方案。一个处理对象可能需要在完成一些工作之后,将结果传递给另一个对象,这个对象接着做一些工作,再转交给下一个处理对象,以此类推。
通常,这种模式是通过定义一个代表处理对象的抽象类来实现的,在抽象类中会定义一个字段来记录后续对象。一旦对象完成它的工作,处理对象就会将它的工作转交给它的后继。
         
E.工厂模式
使用工厂模式,你无需向客户暴露实例化的逻辑就能完成对象的创建。
1.3.调试Lambda
peek方法在分析Stream流水线时,能将中间变量的值输出到日志中。peek在流的每个元素恢复运行之前,插入执行一个动作。但是它不像forEach那样恢复整个流的运行,而是在一个元素上完成操作之后,它只会将操作顺承到流水线中的下一个操作。
2.默认方法
Java 8允许在接口内声明静态方法, Java 8引入了一个新功能,叫默认方法,通过默认方法
你可以指定接口方法的默认实现。接口能提供方法的具体实现。因此,实现接口的类如果不显式地提供该方法的具体实现,就会自动继承默认的实现。
        
A.不同类型的兼容性
◆二进制级的兼容性表示现有的二进制执行文件能无缝持续链接(包括验证、准备和解析)和运行。比如,为接口添加一个方法就是二进制级的兼容,这种方式下,如果新添加的方法不被调用,接口已经实现的方法可以继续运行,不会出现错误;
◆源代码级的兼容性表示引入变化之后,现有的程序依然能成功编译通过;
◆函数行为的兼容性表示变更发生之后,程序接受同样的输入能得到同样的结果。
B.Java 8中的抽象类和抽象接口
首先,一个类只能继承一个抽象类,但是一个类可以实现多个接口;
其次,抽象类可以通过实例变量(字段)保存一个通用状态,而接口是不能有实例变量的。
2.1.默认方法的使用模式
使用默认方法的两种用例: 可选方法和行为的多继承。
A.解决冲突的规则
类的默认方法使用相同的函数签名继承自多个接口,只需要遵守三条准则:
◆首先,类或父类中显式声明的方法,其优先级高于所有的默认方法;
◆其次,第一条无法判断,方法签名又没有区别,那么选择提供最具体实现的默认方法的接口;
◆最后,冲突依旧无法解决,只能在类中覆盖默认方法,指定在类中使用哪一个接口中的方法。
3.用Optional取代null
3.1.null存在的问题
A.null 带来的种种问题
◆它是错误之源:NullPointerException是目前Java程序开发中最典型的异常;
◆它会使你的代码膨胀:它让你的代码充斥着深度嵌套的null检查,代码的可读性糟糕透顶;
◆它自身是毫无意义的:null自身没有任何的语义,尤其是,它代表的是在静态类型语言中以一种错误的方式对缺失变量值的建模;
◆它破坏了Java的哲学:Java一直试图避免让程序员意识到指针的存在,例外是: null指针;
◆它在Java的类型系统上开了个口子:null并不属于任何类型,可被赋值给任意引用类型的变量。
B.null的替代品
Groovy,通过引入安全导航操作符(Safe Navigation Operator,标记为?)可以安全访问可能为null的变量。
Java 8引入了一个名为java.util.Optional<T>的新的类。
3.2.Optional类
A.创建Optional对象
◆Optional.empty:声明一个空的Optional;
◆Optional.of:依据一个非空值创建Optional;
◆Optional.ofNullable:可接受null的Optional,
B.获取数据
         
3.2.使用 Optional 的实战示例
A.用 Optional 封装可能为 null 的值
Optional<Object> value = Optional.ofNullable(map.get("key"));
b.异常使用 Optional
不推荐使用基础类型的Optional,因为基础类型的Optional不支持map、flatMap以及filter方法,而这些却是Optional类最有用的方法。
4.CompletableFuture:组合式异步编程
4.1.并行与并发

4.2.Future 接口
Future接口在Java 5中被引入,它建模了一种异步计算,返回一个执行运算结果的引用,当运算结束后,这个引用被返回给调用方。在Future中触发那些潜在耗时的操作把调用线程解放出来,让它能继续执行其他有价值的工作,不再需要呆呆等待耗时的操作完成。
A.使用 CompletableFuture 构建异步应用
completeExceptionally:使用completeExceptionally的方法会将CompletableFuture内发生问题的异常抛出;
supplyAsync:使用工厂方法supplyAsync创建CompletableFuture,supplyAsync方法接受一个生产(Supplier)作为参数,返回一个CompletableFuture对象,该对象完成异步执行后会读取调用生产者方法的返回值。生产者方法会交由ForkJoinPool池中的某个执行线程(Executor)运行,也可以使用supplyAsync方法的重载版本,传递第二个参数指定不同的执行线程执行生产者方法。
允许你执行器(Executor)进行配置,尤其是线程池的大小,让它以更适合应用需求的方式进行配置,满足程序的要求,而这是并行流API无法提供的。
线程池大小与处理器的利用率之比可以使用下面的公式进行估算:
         
thenCompose:thenCompose方法允许你对两个异步操作进行流水线,第一个操作完成时,将其结果作为参数传递给第二个操作。可以创建两个CompletableFutures对象,对第一个CompletableFuture对象调thenCompose, 并向其传递一个函 数 。当第一个CompletableFuture执行完毕后,它的结果将作为该函数的参数,这个函数的返回值是以第一个CompletableFuture的返回做输入计算出的第二个CompletableFuture对象。
thenComposeAsync:通常而言,名称中不带Async的方法和它的前一个任务一样,在同一个线程中运行;而名称以Async结尾的方法会将后续的任务提交到一个线程池,所以每个任务是由不同的线程处理的。
thenCombine:将两个完全不相干的CompletableFuture对象的结果整合起来,它接收名为BiFunction的第二参数,这个参数定义了当两个CompletableFuture对象完成计算后,结果如何合并
thenCombineAsync:会导致BiFunction中定义的合并操作被提交到线程池中,由另一个任务以异步的方式执行。
thenAccept:操作会在CompletableFuture完成执行后使用它的返回值。
thenAcceptAsync:异步版本的方法会对处理结果的消费者进行调度,从线程池中选择一个新的线程继续执行。
allOf:等待数组中的所有CompletableFuture对象执行完成之后。
anyOf:CompletableFuture对象数组中有任何一个执行完毕就不再等待。
4.3.并行——使用流还是CompletableFutures
并行计算有两种方式:
1.将其转化为并行流,利用map操作开展工作;
2.枚举出集合中的每一个元素,创建新的线程,在CompletableFuture内对其进行操作;
◆如果是计算密集型的操作,并且没有I/O,推荐使用Stream接口,因为实现简单,同时效率也可能是最高的(如果所有的线程都是计算密集型的,那就没有必要创建比处理器核数更多的线程)。
◆如果涉及等待I/O的操作(包括网络连接等待),使用CompletableFuture灵活性更好,可以依据等待/计算,或者W/C的比率设定需要使用的线程数。
5.新的时间和日期API
5.1.LocalDate、 LocalTime、 Instant、 Duration 以及 Period
所有新的时间和日期类都实现了Temporal接口, Temporal接口定义了如何读取和操纵为时间建模的对象的值。Duration的between方法创建两个Temporal对象之间的duration。Duration类主要用于以秒和纳秒衡量时间的长短,Period类以年、月或者日的方式对多个时间单位建模。
         
         
将日期调整到下个周日、下个工作日,或者是本月的最后一天。可以使用重载版本的with方法,向其传递一个提供了更多定制化选择的TemporalAdjuster对象,更加灵活地处理日期。
         
format,格式化以及解析日期时间对象,最重要的类是DateTimeFormatter。和老的java.util.DateFormat相比较,所有的DateTimeFormatter实例都是线程安全的。
DateTimeFormatterBuilder提供了更加细粒度的控制,另外,它还提供了非常强大的解析功能,比如区分大小写的解析、柔性解析(允许解析器使用启发式的机制去解析输入,不精确地匹配指定的模式)、填充,以及在格式器中指定可选节 。
6.代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值