第八章 重构、测试和调试

为改善可读性和灵活性重构代码
  • 改善代码的可读性
    • 重构代码,‘合理的’用Lambda表达式取代匿名类
    • ‘尽量’用方法引用重构Lambda表达式
    • ‘尽量’用Stream API重构命令式的数据处理
  • 增加代码的灵活性
    • 采用函数接口:应用场景
      • 有条件的延迟执行和环绕执行
      • 策略模式场景
      • 模版方法场景
  • 使用Lambda重构面向对象的设计模式
    • 策略模式:策略模式包含一下部分内容
      • 一个代表某个算法的接口
      • 一个或多个接口的具体实现
      • 一个或多个使用策略对象的客户

代码示例见第一部分第二章

  • 模版方法

示例:数据库操作伪代码

  1. public void exec(String sql, Consumer<String> consumer){
  2. openCollection();
  3. consumer.accept(sql);
  4. closeCollection();
  5. }
  6. @Test
  7. public void db(){
  8. exec("insert sql",sql -> System.out.println("执行插入操作:"+sql));
  9. exec("select sql",sql -> System.out.println("执行查询操作:"+sql));
  10. exec("delete sql",sql -> System.out.println("执行删除操作:"+sql));
  11. exec("update sql",sql -> System.out.println("执行更新操作:"+sql));
  12. }
  • 观察者模式
  1. interface Observer{
  2. void notify(String msg);
  3. }
  4. interface Subject{
  5. void register(Observer observer);
  6. void notifyObservers(String msg);
  7. }
  8. class MessageSubject implements Subject{
  9. private final List<Observer> observers = new ArrayList<>();
  10. @Override
  11. public void register(Observer observe) {
  12. observers.add(observe);
  13. }
  14. @Override
  15. public void notifyObservers(String message) {
  16. observers.forEach(o->o.notify(message));
  17. }
  18. }
  19. @Test
  20. public void subsTest(){
  21. MessageSubject messageSubject = new MessageSubject();
  22. messageSubject.register((message)-> System.out.println("订阅者1:收到消息"+message));
  23. messageSubject.register((message)-> System.out.println("订阅者2:收到消息"+message));
  24. messageSubject.register((message)-> System.out.println("订阅者3:收到消息"+message));
  25. messageSubject.register((message)-> System.out.println("订阅者4:收到消息"+message));
  26. messageSubject.notifyObservers("hello java 8");
  27. }
  • 责任链模式
  1. UnaryOperator<String> handler1 = (msg) -> "handler 1 do something:"+msg;
  2. UnaryOperator<String> handler2 = (msg) -> "handler 2 do something:"+msg;
  3. UnaryOperator<String> handler3 = (msg) -> "handler 3 do something:"+msg;
  4. Function<String, String> holder = handler3.andThen(handler2).andThen(handler1);
  5. String result = holder.apply("java 8");
  6. System.out.println(result);

第九章 默认方法

Java8中的接口现在支持在声明方法的同时提供实现,可以通过一下两种方式实现:

  • 在接口内声明静态方法
  • 默认方法,通过默认方法可以指定接口方法的默认实现

总结:解决了一旦接口发生变化,实现这些接口的类也必须发生变化带来的问题,可以以兼容的方式解决Java类库的演进问题。对于工具辅助类,可以将其迁移到接口中。

  • 菱形问题:当实现的接口中包含两个完全一样的默认方法时,需要在实现类中显示的覆盖此方法

第十章 用Optional取代null

  • 作用:处理潜在可能缺失的值
  • 注意:Optional未实现序列化接口,无法进行序列化相关操作
  • 核心方法说明:
  1. public final class Optional<T> {
  2. private static final Optional<?> EMPTY = new Optional<>();
  3. private final T value;
  4. private Optional() {
  5. this.value = null;
  6. }
  7. // 创建一个空的optional对象
  8. public static<T> Optional<T> empty() {
  9. Optional<T> t = (Optional<T>) EMPTY;
  10. return t;
  11. }
  12. // 依据一个非空对象创建一个optional对象,当对象为空时抛出NullPointException
  13. public static <T> Optional<T> of(T value) {
  14. return new Optional<>(value);
  15. }
  16. // 依据一个可空对象创建一个optional对象
  17. public static <T> Optional<T> ofNullable(T value) {
  18. return value == null ? empty() : of(value);
  19. }
  20. // 获取Value,或抛出NoSuchElementException
  21. public T get() {
  22. if (value == null) {
  23. throw new NoSuchElementException("No value present");
  24. }
  25. return value;
  26. }
  27. // 校验值是否存在
  28. public boolean isPresent() {
  29. return value != null;
  30. }
  31. // 如果值存在则执行consumer.accept(value);
  32. public void ifPresent(Consumer<? super T> consumer) {
  33. if (value != null)
  34. consumer.accept(value);
  35. }
  36. // 如果值存在,则执行mapper函数返回对应值类型的optional对象
  37. public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
  38. Objects.requireNonNull(mapper);
  39. if (!isPresent())
  40. return empty();
  41. else {
  42. return Optional.ofNullable(mapper.apply(value));
  43. }
  44. }
  45. // 相对于map拆掉了一层Optional
  46. public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
  47. Objects.requireNonNull(mapper);
  48. if (!isPresent())
  49. return empty();
  50. else {
  51. return Objects.requireNonNull(mapper.apply(value));
  52. }
  53. }
  54. // 如果值存在则返回值,否则返回默认值
  55. public T orElse(T other) {
  56. return value != null ? value : other;
  57. }
  58. // 如果值存在则返回值,否则执行函数返回值
  59. public T orElseGet(Supplier<? extends T> other) {
  60. return value != null ? value : other.get();
  61. }
  62. // 如果值存在则返回值,否则抛出指定异常
  63. public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
  64. if (value != null) {
  65. return value;
  66. } else {
  67. throw exceptionSupplier.get();
  68. }
  69. }
  70. }
  • 简单示例:

    1. // 需求:获取用户名称
    2. // 伪代码,从数据库查询一个用户
    3. User user = getOneUser();
    4. // 原始方式
    5. if(null != user){
    6. String name = user.getName();
    7. if(null !=null && name.length()>0){
    8. return name;
    9. }
    10. }
    11. return "unknown user";
    12. // optional 方式
    13. Optional<User> cd = Optional.ofNullable(null);
    14. Optional<String> optional = cd.map(User::getName);
    15. return optional.orElse("unknown user");
  • 总结:Optional操作不是必须的,不使用Optional也能实现同样的需求,但使用Optional会使得代码更加简洁、优雅、易读和维护,有点像是一层语法糖,对需要操作的对象进行了一次“代理”操作。

    第十一章 CompletableFuture: 组合式编程

    Future接口(Java 5)

    Future使用示意图如下: 第三部分 高效Java 8编程 - 图1代码示例:

    1. @Test
    2. public void asyncTest() throws ExecutionException, InterruptedException, TimeoutException {
    3. ExecutorService executor = Executors.newCachedThreadPool();
    4. Future<String> future = executor.submit(()->{
    5. System.out.println("hello future :"+Thread.currentThread().getName());
    6. Thread.sleep(1000);
    7. return "asynchronous exec end";
    8. });
    9. System.out.println("main thread:"+Thread.currentThread().getName());
    10. future.get(2, TimeUnit.SECONDS);
    11. }
  • CompletableFuture

代码示例:

  1. @Test
  2. public void completableFutureTest() throws Exception{
  3. CompletableFuture<String> completableFuture = new CompletableFuture<>();
  4. ExecutorService executor = Executors.newCachedThreadPool();
  5. executor.submit(()->{
  6. try {
  7. // 耗时操作处理
  8. Thread.sleep(1000);
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. }
  12. System.out.println("generate result");
  13. completableFuture.complete("async result:: "+Thread.currentThread().getName());
  14. });
  15. try {
  16. // 耗时操作处理
  17. Thread.sleep(1000);
  18. System.out.println("main thread do other thing:"+Thread.currentThread().getName());
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }
  22. System.out.println("try get result");
  23. String s = completableFuture.get();
  24. System.out.println("result: " +s);
  25. }

第十二章 新的日期和时间API

  • LocalDate
  1. // 以及已有日期创建
  2. LocalDate of = LocalDate.of(2019, 11, 15);
  3. // 获取当前日志
  4. LocalDate today = LocalDate.now();
  5. // 获取当前年
  6. int year = today.getYear();
  7. // 获取当前月
  8. int monthValue = today.getMonthValue();
  9. // 获取当前月有多少天
  10. int lengthOfMonth = today.lengthOfMonth();
  11. // 获取当前年有多少天
  12. int lengthOfYear = today.lengthOfYear();
  13. // 获取当前是周几
  14. DayOfWeek dayOfWeek = today.getDayOfWeek();
  15. // 获取当前是一年中的第几天
  16. int dayOfYear = today.getDayOfYear();
  • LocalTime
  1. // 创建
  2. LocalTime localTime = LocalTime.of(2,34,34);
  3. LocalTime now = LocalTime.now();
  4. int hour = now.getHour();
  5. int minute = now.getMinute();
  6. int second = now.getSecond();
  • LocalDate和LocalTime的合体LocalDateTime
  • 操作解析和格式化日期
  1. LocalDateTime now = LocalDateTime.now();
  2. // 获取上月的日期 注意:LocalDateTime为不可变对象,下述方法都是重新创建出Local对象
  3. // 方式1
  4. LocalDateTime lastMonthDateTime = now.minusMonths(1);
  5. // 方式2
  6. LocalDateTime lastMonthDateTime2 = now.withMonth(now.getMonthValue() - 1);
  7. // 获取10天以后的日期
  8. LocalDateTime after10DaysTime = now.plusDays(10);
  • TemporalAdjusters
  1. LocalDateTime now = LocalDateTime.now();
  2. // 获取当前月第三周的周末
  3. LocalDateTime with = now.with(TemporalAdjusters.dayOfWeekInMonth(3, DayOfWeek.SUNDAY));
  4. // 获取当前月的第一天
  5. LocalDateTime firstDayOfMonth = now.with(TemporalAdjusters.firstDayOfMonth());
  6. // 获取下一月的第一天
  7. LocalDateTime firstDayOfNextMonth = now.with(TemporalAdjusters.firstDayOfNextMonth());
  8. // 获取当前年的第一天
  9. LocalDateTime firstDayOfYear = now.with(TemporalAdjusters.firstDayOfYear());
  10. // 获取下一年的第一天
  11. LocalDateTime firstDayOfNextYear = now.with(TemporalAdjusters.firstDayOfNextYear());
  12. // 获取当前月第一周的周末 等同于 now.with(TemporalAdjusters.dayOfWeekInMonth(1, DayOfWeek.SUNDAY));
  13. LocalDateTime firstInMonth = now.with(TemporalAdjusters.firstInMonth(DayOfWeek.SUNDAY));
  14. // 获取当前月的最后一天
  15. LocalDateTime lastDayOfMonth = now.with(TemporalAdjusters.lastDayOfMonth());
  • 格式化输出
  1. LocalDateTime now = LocalDateTime.now();
  2. // 20191115
  3. System.out.println(now.format(DateTimeFormatter.BASIC_ISO_DATE));
  4. // 2019-11-15T17:32:21.605
  5. System.out.println(now.format(DateTimeFormatter.ISO_DATE_TIME));
  6. // 2019-11-15 17:32:21
  7. System.out.println(now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));