第八章 重构、测试和调试
为改善可读性和灵活性重构代码
- 改善代码的可读性
- 重构代码,‘合理的’用Lambda表达式取代匿名类
- ‘尽量’用方法引用重构Lambda表达式
- ‘尽量’用Stream API重构命令式的数据处理
- 增加代码的灵活性
- 采用函数接口:应用场景
- 有条件的延迟执行和环绕执行
- 策略模式场景
- 模版方法场景
- 采用函数接口:应用场景
- 使用Lambda重构面向对象的设计模式
- 策略模式:策略模式包含一下部分内容
- 一个代表某个算法的接口
- 一个或多个接口的具体实现
- 一个或多个使用策略对象的客户
- 策略模式:策略模式包含一下部分内容
代码示例见第一部分第二章
- 模版方法
示例:数据库操作伪代码
public void exec(String sql, Consumer<String> consumer){openCollection();consumer.accept(sql);closeCollection();}@Testpublic void db(){exec("insert sql",sql -> System.out.println("执行插入操作:"+sql));exec("select sql",sql -> System.out.println("执行查询操作:"+sql));exec("delete sql",sql -> System.out.println("执行删除操作:"+sql));exec("update sql",sql -> System.out.println("执行更新操作:"+sql));}
- 观察者模式
interface Observer{void notify(String msg);}interface Subject{void register(Observer observer);void notifyObservers(String msg);}class MessageSubject implements Subject{private final List<Observer> observers = new ArrayList<>();@Overridepublic void register(Observer observe) {observers.add(observe);}@Overridepublic void notifyObservers(String message) {observers.forEach(o->o.notify(message));}}@Testpublic void subsTest(){MessageSubject messageSubject = new MessageSubject();messageSubject.register((message)-> System.out.println("订阅者1:收到消息"+message));messageSubject.register((message)-> System.out.println("订阅者2:收到消息"+message));messageSubject.register((message)-> System.out.println("订阅者3:收到消息"+message));messageSubject.register((message)-> System.out.println("订阅者4:收到消息"+message));messageSubject.notifyObservers("hello java 8");}
- 责任链模式
UnaryOperator<String> handler1 = (msg) -> "handler 1 do something:"+msg;UnaryOperator<String> handler2 = (msg) -> "handler 2 do something:"+msg;UnaryOperator<String> handler3 = (msg) -> "handler 3 do something:"+msg;Function<String, String> holder = handler3.andThen(handler2).andThen(handler1);String result = holder.apply("java 8");System.out.println(result);
第九章 默认方法
Java8中的接口现在支持在声明方法的同时提供实现,可以通过一下两种方式实现:
- 在接口内声明静态方法
- 默认方法,通过默认方法可以指定接口方法的默认实现
总结:解决了一旦接口发生变化,实现这些接口的类也必须发生变化带来的问题,可以以兼容的方式解决Java类库的演进问题。对于工具辅助类,可以将其迁移到接口中。
- 菱形问题:当实现的接口中包含两个完全一样的默认方法时,需要在实现类中显示的覆盖此方法
第十章 用Optional取代null
- 作用:处理潜在可能缺失的值
- 注意:Optional未实现序列化接口,无法进行序列化相关操作
- 核心方法说明:
public final class Optional<T> {private static final Optional<?> EMPTY = new Optional<>();private final T value;private Optional() {this.value = null;}// 创建一个空的optional对象public static<T> Optional<T> empty() {Optional<T> t = (Optional<T>) EMPTY;return t;}// 依据一个非空对象创建一个optional对象,当对象为空时抛出NullPointExceptionpublic static <T> Optional<T> of(T value) {return new Optional<>(value);}// 依据一个可空对象创建一个optional对象public static <T> Optional<T> ofNullable(T value) {return value == null ? empty() : of(value);}// 获取Value,或抛出NoSuchElementExceptionpublic T get() {if (value == null) {throw new NoSuchElementException("No value present");}return value;}// 校验值是否存在public boolean isPresent() {return value != null;}// 如果值存在则执行consumer.accept(value);public void ifPresent(Consumer<? super T> consumer) {if (value != null)consumer.accept(value);}// 如果值存在,则执行mapper函数返回对应值类型的optional对象public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {Objects.requireNonNull(mapper);if (!isPresent())return empty();else {return Optional.ofNullable(mapper.apply(value));}}// 相对于map拆掉了一层Optionalpublic<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {Objects.requireNonNull(mapper);if (!isPresent())return empty();else {return Objects.requireNonNull(mapper.apply(value));}}// 如果值存在则返回值,否则返回默认值public T orElse(T other) {return value != null ? value : other;}// 如果值存在则返回值,否则执行函数返回值public T orElseGet(Supplier<? extends T> other) {return value != null ? value : other.get();}// 如果值存在则返回值,否则抛出指定异常public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {if (value != null) {return value;} else {throw exceptionSupplier.get();}}}
简单示例:
// 需求:获取用户名称// 伪代码,从数据库查询一个用户User user = getOneUser();// 原始方式if(null != user){String name = user.getName();if(null !=null && name.length()>0){return name;}}return "unknown user";// optional 方式Optional<User> cd = Optional.ofNullable(null);Optional<String> optional = cd.map(User::getName);return optional.orElse("unknown user");
总结:Optional操作不是必须的,不使用Optional也能实现同样的需求,但使用Optional会使得代码更加简洁、优雅、易读和维护,有点像是一层语法糖,对需要操作的对象进行了一次“代理”操作。
第十一章 CompletableFuture: 组合式编程
Future接口(Java 5)
Future使用示意图如下:
代码示例:
@Testpublic void asyncTest() throws ExecutionException, InterruptedException, TimeoutException {ExecutorService executor = Executors.newCachedThreadPool();Future<String> future = executor.submit(()->{System.out.println("hello future :"+Thread.currentThread().getName());Thread.sleep(1000);return "asynchronous exec end";});System.out.println("main thread:"+Thread.currentThread().getName());future.get(2, TimeUnit.SECONDS);}
CompletableFuture
代码示例:
@Testpublic void completableFutureTest() throws Exception{CompletableFuture<String> completableFuture = new CompletableFuture<>();ExecutorService executor = Executors.newCachedThreadPool();executor.submit(()->{try {// 耗时操作处理Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("generate result");completableFuture.complete("async result:: "+Thread.currentThread().getName());});try {// 耗时操作处理Thread.sleep(1000);System.out.println("main thread do other thing:"+Thread.currentThread().getName());} catch (InterruptedException e) {e.printStackTrace();}System.out.println("try get result");String s = completableFuture.get();System.out.println("result: " +s);}
第十二章 新的日期和时间API
- LocalDate
// 以及已有日期创建LocalDate of = LocalDate.of(2019, 11, 15);// 获取当前日志LocalDate today = LocalDate.now();// 获取当前年int year = today.getYear();// 获取当前月int monthValue = today.getMonthValue();// 获取当前月有多少天int lengthOfMonth = today.lengthOfMonth();// 获取当前年有多少天int lengthOfYear = today.lengthOfYear();// 获取当前是周几DayOfWeek dayOfWeek = today.getDayOfWeek();// 获取当前是一年中的第几天int dayOfYear = today.getDayOfYear();
- LocalTime
// 创建LocalTime localTime = LocalTime.of(2,34,34);LocalTime now = LocalTime.now();int hour = now.getHour();int minute = now.getMinute();int second = now.getSecond();
- LocalDate和LocalTime的合体LocalDateTime
- 操作解析和格式化日期
LocalDateTime now = LocalDateTime.now();// 获取上月的日期 注意:LocalDateTime为不可变对象,下述方法都是重新创建出Local对象// 方式1LocalDateTime lastMonthDateTime = now.minusMonths(1);// 方式2LocalDateTime lastMonthDateTime2 = now.withMonth(now.getMonthValue() - 1);// 获取10天以后的日期LocalDateTime after10DaysTime = now.plusDays(10);
- TemporalAdjusters
LocalDateTime now = LocalDateTime.now();// 获取当前月第三周的周末LocalDateTime with = now.with(TemporalAdjusters.dayOfWeekInMonth(3, DayOfWeek.SUNDAY));// 获取当前月的第一天LocalDateTime firstDayOfMonth = now.with(TemporalAdjusters.firstDayOfMonth());// 获取下一月的第一天LocalDateTime firstDayOfNextMonth = now.with(TemporalAdjusters.firstDayOfNextMonth());// 获取当前年的第一天LocalDateTime firstDayOfYear = now.with(TemporalAdjusters.firstDayOfYear());// 获取下一年的第一天LocalDateTime firstDayOfNextYear = now.with(TemporalAdjusters.firstDayOfNextYear());// 获取当前月第一周的周末 等同于 now.with(TemporalAdjusters.dayOfWeekInMonth(1, DayOfWeek.SUNDAY));LocalDateTime firstInMonth = now.with(TemporalAdjusters.firstInMonth(DayOfWeek.SUNDAY));// 获取当前月的最后一天LocalDateTime lastDayOfMonth = now.with(TemporalAdjusters.lastDayOfMonth());
- 格式化输出
LocalDateTime now = LocalDateTime.now();// 20191115System.out.println(now.format(DateTimeFormatter.BASIC_ISO_DATE));// 2019-11-15T17:32:21.605System.out.println(now.format(DateTimeFormatter.ISO_DATE_TIME));// 2019-11-15 17:32:21System.out.println(now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
