三、高效Java 8编程

第8章 重构、测试和调试

Java 8的新特性也可以帮助提升代码的可读性:
     使用Java 8,你可以减少冗长的代码,让代码更易于理解
     通过方法引用和Stream API,你的代码会变得更直观

介绍三种简单的重构,利用Lambda表达式、方法引用以及Stream改善程序代码的可读性:
     重构代码,用Lambda表达式取代匿名类
     用方法引用重构Lambda表达式
     用Stream API重构命令式的数据处理

增加代码的灵活性:

1. 采用函数接口
    2. 有条件的延迟执行
    3. 环绕执行
下面回顾一下这一章的主要内容。
     Lambda表达式能提升代码的可读性和灵活性。
     如果你的代码中使用了匿名类,尽量用Lambda表达式替换它们,但是要注意二者间语义的微妙差别,比如关键字this,以及变量隐藏。
     跟Lambda表达式比起来,方法引用的可读性更好 。
     尽量使用Stream API替换迭代式的集合处理。
     Lambda表达式有助于避免使用面向对象设计模式时容易出现的僵化的模板代码,典型的比如策略模式、模板方法、观察者模式、责任链模式,以及工厂模式。
     即使采用了Lambda表达式,也同样可以进行单元测试,但是通常你应该关注使用了Lambda表达式的方法的行为。
     尽量将复杂的Lambda表达式抽象到普通方法中。
     Lambda表达式会让栈跟踪的分析变得更为复杂。
     流提供的peek方法在分析Stream流水线时,能将中间变量的值输出到日志中,是非常有用的工具。

第9章 默认方法

下面是本章你应该掌握的关键概念。
     Java 8中的接口可以通过默认方法和静态方法提供方法的代码实现。
     默认方法的开头以关键字default修饰,方法体与常规的类方法相同。
     向发布的接口添加抽象方法不是源码兼容的。
     默认方法的出现能帮助库的设计者以后向兼容的方式演进API。
     默认方法可以用于创建可选方法和行为的多继承。
     我们有办法解决由于一个类从多个接口中继承了拥有相同函数签名的方法而导致的冲突。
     类或者父类中声明的方法的优先级高于任何默认方法。如果前一条无法解决冲突,那就选择同函数签名的方法中实现得最具体的那个接口的方法。
     两个默认方法都同样具体时,你需要在类中覆盖该方法,显式地选择使用哪个接口中提供的默认方法。

第10章 用Optional取代null

这一章中,你学到了以下的内容。
     null引用在历史上被引入到程序设计语言中,目的是为了表示变量值的缺失。
     Java 8中引入了一个新的类java.util.Optional<T>,对存在或缺失的变量值进行建模。
     你可以使用静态工厂方法Optional.empty、 Optional.of以及Optional.ofNullable创建Optional对象。
     Optional类支持多种方法,比如map、 flatMap、 filter,它们在概念上与Stream类中对应的方法十分相似。
     使用Optional会迫使你更积极地解引用Optional对象,以应对变量值缺失的问题,最终,你能更有效地防止代码中出现不期而至的空指针异常。
     使用Optional能帮助你设计更好的API,用户只需要阅读方法签名,就能了解该方法是否接受一个Optional类型的值。

第11章 CompletableFuture:组合式  异步编程

示例:

public class Shop {
public double getPrice(String product) {
// 待实现
}
} // 模拟1秒钟延迟的方法
public static void delay() {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} // 在getPrice方法中引入一个模拟的延迟
public double getPrice(String product) {
return calculatePrice(product);
}
private double calculatePrice(String product) {
delay();
return random.nextDouble() * product.charAt(0) + product.charAt(1);
}
// 将同步方法转换为异步方法及实现
public Future<Double> getPriceAsync(String product) { ... } public Future<Double> getPriceAsync(String product) {
CompletableFuture<Double> futurePrice = new CompletableFuture<>();
new Thread( () -> {
double price = calculatePrice(product);
futurePrice.complete(price);
}).start();
return futurePrice;
} // 使用异步API
Shop shop = new Shop("BestShop");
long start = System.nanoTime();
Future<Double> futurePrice = shop.getPriceAsync("my favorite product");
long invocationTime = ((System.nanoTime() - start) / 1_000_000);
System.out.println("Invocation returned after " + invocationTime + " msecs");
// 执行更多任务,比如查询其他商店
doSomethingElse();
// 在计算商品价格的同时
try {
double price = futurePrice.get();
System.out.printf("Price is %.2f%n", price);
} catch (Exception e) {
throw new RuntimeException(e);
}
long retrievalTime = ((System.nanoTime() - start) / 1_000_000);
System.out.println("Price returned after " + retrievalTime + " msecs");

错误处理:

public Future<Double> getPriceAsync(String product) {
CompletableFuture<Double> futurePrice = new CompletableFuture<>();
new Thread( () -> {
try {
double price = calculatePrice(product);
// 如果价格计算正常结束,完成Future操作并设置商品价格
futurePrice.complete(price);
} catch (Exception ex) {
// 否则就抛出导致失败的异常,完成这次Future操作
futurePrice.completeExceptionally(ex);
}
}).start();
return futurePrice;
} // 使用工厂方法supplyAsync创建CompletableFuture
public Future<Double> getPriceAsync(String product) {
return CompletableFuture.supplyAsync(() -> calculatePrice(product));
}

使用 CompletableFuture 发起异步请求:

List<CompletableFuture<String>> priceFutures =
shops.stream()
.map(shop -> CompletableFuture.supplyAsync(
() -> String.format("%s price is %.2f",shop.getName(), shop.getPrice(product))))
.collect(toList());

使用定制的执行器

private final Executor executor = Executors.newFixedThreadPool(
Math.min(shops.size(), 100), new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
}
}); CompletableFuture.supplyAsync(() -> shop.getName() + " price is "
+ shop.getPrice(product), executor);

CompletableFuture总结的有点潦草,如实战的话,还需在深入研究一下。

这一章中,你学到的内容如下。
     执行比较耗时的操作时,尤其是那些依赖一个或多个远程服务的操作,使用异步任务可以改善程序的性能,加快程序的响应速度。
     你应该尽可能地为客户提供异步API。使用CompletableFuture类提供的特性,你能够轻松地实现这一目标。
     CompletableFuture类还提供了异常管理的机制,让你有机会抛出/管理异步任务执行中发生的异常。
     将同步API的调用封装到一个CompletableFuture中,你能够以异步的方式使用其结果。
     如果异步任务之间相互独立,或者它们之间某一些的结果是另一些的输入,你可以将这些异步任务构造或者合并成一个。
     你可以为CompletableFuture注册一个回调函数,在Future执行完毕或者它们计算的结果可用时,针对性地执行一些程序。
     你可以决定在什么时候结束程序的运行,是等待由CompletableFuture对象构成的列表中所有的对象都执行完毕,还是只要其中任何一个首先完成就中止程序的运行。

第12章 新的日期和时间API

LocalDate、 LocalTime、 Instant、 Duration 以及 Period的使用

LocalDate、 LocalTime示例:

// 创建一个LocalDate对象并读取其值
LocalDate date = LocalDate.of(2014, 3, 18);
int year = date.getYear();
Month month = date.getMonth();
int day = date.getDayOfMonth();
DayOfWeek dow = date.getDayOfWeek();
int len = date.lengthOfMonth();
boolean leap = date.isLeapYear();
LocalDate today = LocalDate.now(); // 使用TemporalField读取LocalDate的值
int year = date.get(ChronoField.YEAR);
int month = date.get(ChronoField.MONTH_OF_YEAR);
int day = date.get(ChronoField.DAY_OF_MONTH); // 创建LocalTime并读取其值
LocalTime time = LocalTime.of(13, 45, 20);
int hour = time.getHour();
int minute = time.getMinute();
int second = time.getSecond(); LocalDate date = LocalDate.parse("2014-03-18");
LocalTime time = LocalTime.parse("13:45:20"); // 直接创建LocalDateTime对象,或者通过合并日期和时间的方式创建
// 2014-03-18T13:45:20
LocalDateTime dt1 = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45, 20);
LocalDateTime dt2 = LocalDateTime.of(date, time);
LocalDateTime dt3 = date.atTime(13, 45, 20);
LocalDateTime dt4 = date.atTime(time);
LocalDateTime dt5 = time.atDate(date); LocalDate date1 = dt1.toLocalDate();
LocalTime time1 = dt1.toLocalTime();

Instant示例:

通过向静态工厂方法ofEpochSecond传递一个代表秒数的值创建一个该类的实例。

int day = Instant.now().get(ChronoField.DAY_OF_MONTH);
它会抛出下面这样的异常:
java.time.temporal.UnsupportedTemporalTypeException: Unsupported field:DayOfMonth
但是你可以通过Duration和Period类使用Instant, 接下来我们会对这部分内容进行介绍。

定义 Duration 或 Period

Duration d1 = Duration.between(time1, time2);
Duration d1 = Duration.between(dateTime1, dateTime2);
Duration d2 = Duration.between(instant1, instant2); Period tenDays = Period.between(LocalDate.of(2014, 3, 8), LocalDate.of(2014, 3, 18)); // 创建Duration和Period对象
Duration threeMinutes = Duration.ofMinutes(3);
Duration threeMinutes = Duration.of(3, ChronoUnit.MINUTES);
Period tenDays = Period.ofDays(10);
Period threeWeeks = Period.ofWeeks(3);
Period twoYearsSixMonthsOneDay = Period.of(2, 6, 1);

操纵、解析和格式化日期

处理不同的时区和历法:

ZoneId romeZone = ZoneId.of("Europe/Rome");

ZoneId zoneId = TimeZone.getDefault().toZoneId();

// 为时间点添加时区信息
LocalDate date = LocalDate.of(2014, Month.MARCH, 18);
ZonedDateTime zdt1 = date.atStartOfDay(romeZone);
LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45);
ZonedDateTime zdt2 = dateTime.atZone(romeZone);
Instant instant = Instant.now();
ZonedDateTime zdt3 = instant.atZone(romeZone);

// 通过ZoneId,你还可以将LocalDateTime转换为Instant:
LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45);
Instant instantFromDateTime = dateTime.toInstant(romeZone); // 你也可以通过反向的方式得到LocalDateTime对象:
Instant instant = Instant.now();
LocalDateTime timeFromInstant = LocalDateTime.ofInstant(instant, romeZone); /**
* 利用和 UTC/格林尼治时间的固定偏差计算时区
*/
ZoneOffset newYorkOffset = ZoneOffset.of("-05:00"); LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45);
OffsetDateTime dateTimeInNewYork = OffsetDateTime.of(date, newYorkOffset); /**
* 使用别的日历系统
*/
LocalDate date = LocalDate.of(2014, Month.MARCH, 18);
JapaneseDate japaneseDate = JapaneseDate.from(date); Chronology japaneseChronology = Chronology.ofLocale(Locale.JAPAN);
ChronoLocalDate now = japaneseChronology.dateNow(); // 伊斯兰教日历
HijrahDate ramadanDate = HijrahDate.now().with(ChronoField.DAY_OF_MONTH, 1)
.with(ChronoField.MONTH_OF_YEAR, 9);
System.out.println("Ramadan starts on " + IsoChronology.INSTANCE.date(ramadanDate)
+ " and ends on " + IsoChronology.INSTANCE
.date(ramadanDate.with(TemporalAdjusters.lastDayOfMonth())));

这一章中,你应该掌握下面这些内容。
     Java 8之前老版的java.util.Date类以及其他用于建模日期时间的类有很多不一致及设计上的缺陷,包括易变性以及糟糕的偏移值、默认值和命名。
     新版的日期和时间API中,日期时间对象是不可变的。
     新的API提供了两种不同的时间表示方式,有效地区分了运行时人和机器的不同需求。
     你可以用绝对或者相对的方式操纵日期和时间,操作的结果总是返回一个新的实例,老的日期时间对象不会发生变化。
     TemporalAdjuster让你能够用更精细的方式操纵日期,不再局限于一次只能改变它的一个值,并且你还可按照需求定义自己的日期转换器。
     你现在可以按照特定的格式需求,定义自己的格式器,打印输出或者解析日期时间对象。这些格式器可以通过模板创建,也可以自己编程创建,并且它们都是线程安全的。
     你可以用相对于某个地区/位置的方式,或者以与UTC/格林尼治时间的绝对偏差的方式表示时区,并将其应用到日期时间对象上,对其进行本地化。
     你现在可以使用不同于ISO-8601标准系统的其他日历系统了。

Java 8实战之读书笔记四:高效Java 8编程的更多相关文章

  1. 《Java 8实战》读书笔记系列——第三部分:高效Java 8编程(四):使用新的日期时间API

    https://www.lilu.org.cn/https://www.lilu.org.cn/ 第十二章:新的日期时间API 在Java 8之前,我们常用的日期时间API是java.util.Dat ...

  2. Java 8实战之读书笔记一:内容简介

    本书的主要内容如下:  如何使用Java 8新增的强大特性  如何编写能有效利用多核架构的程序  重构.测试和调试  怎样高效地应用函数式编程 目录: 第一部分 基础知识 第1 章 为什么要关心Jav ...

  3. Java 8实战之读书笔记三:函数式数据处理

    二.函数式数据处理 第4章 引入流 流是Java API的新成员,它允许你以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现). 示例: import static java.uti ...

  4. Java 8实战之读书笔记五:超越Java 8

    四.超越Java 8 第13章 函数式的思考 下面是这一章中你应该掌握的关键概念.      从长远看,减少共享的可变数据结构能帮助你降低维护和调试程序的代价.      函数式编程支持无副作用的 ...

  5. Java 8实战之读书笔记二:基础知识

    好记性不如烂笔头,整理一些个人觉得比较重要的东西. 一.基础知识 第1章 为什么要关心Java 8 Java 8提供了一个新的API(称为"流", Stream),它支持许多处理数 ...

  6. 《Java并发编程实战》读书笔记一 -- 简介

    <Java并发编程实战>读书笔记一 -- 简介 并发的历史 并发的历史,也是人类利用有限的资源去提高生产效率的一个的例子. 设想现在有台计算机,这台计算机具有以下的资源: 单核CPU一个 ...

  7. 《Apache Kafka 实战》读书笔记-认识Apache Kafka

    <Apache Kafka 实战>读书笔记-认识Apache Kafka 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.kafka概要设计 kafka在设计初衷就是 ...

  8. 《Go并发编程实战》读书笔记-初识Go语言

    <Go并发编程实战>读书笔记-初识Go语言 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 在讲解怎样用Go语言之前,我们先介绍Go语言的特性,基础概念和标准命令. 一. ...

  9. 《Apache kafka实战》读书笔记-kafka集群监控工具

    <Apache kafka实战>读书笔记-kafka集群监控工具 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 如官网所述,Kafka使用基于yammer metric ...

随机推荐

  1. Window Server 2008 R2 FTP服务用户隔离

    Window Server 2008 R2 FTP服务用户隔离 原题:安装FTP服务,新建一个FTP站点,主目录为C:\ftproot,通过适当技术实现用户soft1 与soft2通过匿名方式登录FT ...

  2. 为什么有线宽带提供商获得ASN非常重要?

    光纤和同轴电缆的组合(数据有线电视服务接口规范),由此产生的网络在世界引入了高速互联网接入.我们能够从网络运营中心向家庭用户提供10Mbps的下载速度. 拥有自己的自治系统编号(ASN)和IP块意味着 ...

  3. hdu 5963:朋友

    刚看到这题时感觉是树上博弈,然后我开始用一维的数据找规律.发现在一维的树上,如果把各边的值合在一起当成一个二进制数,那么,ans只与奇偶性有关,于是,我提出了一个比较大胆的假设:若连接在root上的所 ...

  4. 让Flash内心崩溃的HTML5历史

    对于HTML5,在今天这个互联网时代,大部分人应该至少都听说过这个名字,或许很多人对HTML5的了解都起于一句话:FLASH杀手. HTML5其实早已不是什么新鲜的事物了,其最初的雏形早在2004年就 ...

  5. Vue的思考扩展

    1.Vue是如何实现数据双向绑定的 1.1.实现双向绑定的基本原理 数据驱动:Vue会通过Dircetives指令,对DOM做一层封装,当数据发生改变会通知指令去修改对应的DOM,数据驱动DOM变化, ...

  6. CSS中属性百分比的基准点

    1.属性百分比的基准点 1.1.基于包含块 以下的关于包含块(含块)的概念,不能简单地理解成是父元素. 如果是静态定位和相对定位,包含块一般就是其父元素.但是对于绝对定位的元素,包含块应该是离它最近的 ...

  7. <自动化测试>之<自动获取手机短信验证码>

    第一次写博,最近解决了做自动化测试短信验证码自动获取填入的方法减少了脚本的人工干预,并非拦截短信,所以不存在安全警报提醒,拿出来分享给大家,有感兴趣的大家可以加Q1856100 目前在职测试开发,,写 ...

  8. Trailing Zeroes (III) -;lightoj 1138

    Trailing Zeroes (III)   PDF (English) Statistics Forum Time Limit: 2 second(s) Memory Limit: 32 MB Y ...

  9. HDU | Friend Chains-4460 SPFA

    题目: Problem DescriptionFor a group of people, there is an idea that everyone is equals to or less th ...

  10. 《数据结构与算法(C语言版)》严蔚敏 | 第四章课本案例

    //二叉树的顺序存储表示 #define MAXTSIZE 100 typedef TElemtype SqBiTree[MAXTSIZE]; SqBiTree bt; //二叉树的二叉链表存储表示 ...