第 8 章
重构、测试和调试
改善代码?
- 集合类的操作→stream;
- 匿名类->Lambda表达式(注意:匿名类的this指匿名类自身,而lambda的this指的是lambda"对象"(把lambda看做成员变量)所在的类,因此Lambda{...}内部不能定义与外部相同名字的变量,会编译错误)
eg:
{...
int a = 10;
Runnable r = new Runnable(){int a = 10;...} //**编译错误**
}
- 方法引用代替{...}中的语句,将{...}中的语句抽象成方法,这样就可以使用方法引用;
- 在作为方法参数时,匿名类的类型是确定的(因为是new出来的),但Lambda类型是"匹配出来的",因此在函数涉及重载的时候,lambda的类型会不确定;
eg:
interface Task {
public void excute();
public void doSth(Runnable r) {
r.run();
}
//如果传入lambda则它的类型无法指定;
public void doSth(Task t) {
t.excute();
}
}
- Lambda在设计模式中的应用,如策略策略设计模式/模板方法模式-替换定义的多个类、职责链模式-Function .andThen()、工厂方法模式-把lambda ClassA::new,ClassB::new看做对象放到一个map里面,用get通过名字去取不同类的对象;
- lambda不方便调试(没有类名),在stream操作中可以使用peek方法,有点像断点调试;
eg:
@Test
public void func_04() {
System.out.println("step-1:");
Stream.of(1, 2, 3, 4, 5).map(i -> i + 10).forEach(System.out::println);//使用forEach调试,但查看结果,不能查看每一步的结果;
System.out.println("step-2:");
Stream.of(1, 2, 3, 4, 5).map(i -> i + 10).filter(i -> i < 13).forEach(System.out::println);//使用forEach调试,但查看结果,不能查看每一步的结果,只能一步步地打印,很麻烦;
System.out.println("use-peak():");
Stream.of(1, 2, 3, 4, 5).map(i -> i + 10).peek(i -> System.out.println("step-1:" + i)).filter(i -> i < 13).peek(i -> System.out.println("step-2:" + i)).count();//使用peak调试,注意最后要加上"终端操作"
}
第 9 章
default方法
-
为什么要新加default方法?
——为了兼容类库,为了在接口中能新加入方法而不需要重构(在其子类中实现新抽象方法)以前的代码; -
default方法冲突的3条规则?
(1) 当两个父类接口中,有一个的default方法更加"具体"(这里B继承A,比A的hello方法更具体),会继承那个更具体的方法;
(2) 子类继承extends一个父类并且implements两个具有相同default方法的父类接口,extends类优先;
(3) 子类通过在覆盖的方法内,显示的调用某个父类的default方法来避免冲突,Father_A.super.hello();
eg:
public class TestDefaultMethod_02 {
//测试default方法遇上菱形继承
public static void main(String[] args) {
//default遇上多继承(相同的default方法声明)
new TDM_C().hello();//(1)当两个父类接口中,有一个的default方法更加"具体"(这里B继承A,比A的hello方法更具体),会继承那个更具体的方法;
new TDM_E().hello();//(2)子类继承extends一个父类并且implements两个具有相同default方法的父类接口,extends类优先;
new TDM_H().hello();//(3)子类通过在覆盖的方法内,显示的调用某个父类的default方法来避免冲突,Father_A.super.hello();
}
}
interface TDM_A {
default void hello() {
System.out.println("hello A.");
}
}
interface TDM_B extends TDM_A {//**class B的方法比A的hello更"具体"**
default void hello() {
System.out.println("hello B.");
}
}
class TDM_C implements TDM_A, TDM_B {
}
class TDM_D {
public void hello() {
System.out.println("hello D.");
}
}
class TDM_E extends TDM_D implements TDM_A, TDM_B {//**类优先,继承D的方法**
}
interface TDM_f {
default void hello() {
System.out.println("hello f.");
}
}
interface TDM_G {
default void hello() {
System.out.println("hello G.");
}
}
class TDM_H implements TDM_f, TDM_G {
public void hello() {
TDM_f.super.hello();**//显示调用父类接口的default方法,避免冲突;**
TDM_G.super.hello();
}
}
第 10 章
用Optional取代null
-
非空判断null带来的问题
多层嵌套非空判断;可读性差;nul可以赋值给任意类型对对象; -
Optional是一个存放变量的容器,通过泛型与变量类型绑定,eg:Optional<String>替换String,空值为Optional.empty(),不再是一个null;Optional的目的不是撤掉消除null,而是能清晰地界定出变量值的缺失原因,是结构/算法逻辑/数据本身的问题,能帮助设计出更加普适的API;
-
Optional用法
初始化:
Optional.empty();
Optional.of(T t);
Optional.ofNullable(T t);
eg:
@Test
public void func_01() {//测试Optional对象初始化
Optional<TO_Person> p1 = **Optional.empty() **;//(1)申明一个值为null的Optional
p1.ifPresent(System.out::print);//加了这一句就不需要toString里面的非空判断
TO_Person person = new TO_Person("Li", 18);
Optional<TO_Person> p2 = **Optional.of(person) **;//(2)of方法,传递一个对象(值)
System.out.println(p2.isPresent());
person = null;//**修改person不会影响Optional的第一次赋值**(存的是副本-深度复制)
System.out.println(p2.isPresent());
// p2 = Optional.of(null);
// System.out.println(p2.isPresent());//这里会NullPointerException,因为of方法不接受空值null;
Optional<TO_Person> p3 = **Optional. ****ofNullable(null) **;//(3)ofNullable接收空值null;
}
map/flatMap方法
@Test
public void func_02() {
TO_Person person = new TO_Person("Lee", 28);
Optional<TO_Person> p1 = Optional.of(person);
Optional<String> p2 = p1. **flatMap **(TO_Person::getName);//**(1)* map方法与流Stream的map方法用法一样**;
// 需求:需要获取一个Person对象的name,根据这个name来新new一个Person对象,打印它的age属性
//**这种操作像一个"管道"**,先对person对象map成String name,再将String给map成person对象,最后再map成int age;
// Optional<Integer> p3 = p1.map(TO_Person::getName).map(TO_Person::getPersonByName).map(TO_Person::getAge); //**编译错误**,**因为第二个map的传入的对象类型是Optional<Optional<String>>**;
// 之前在Stream中遇到类似的问题,**即流元素本身也是个流,需要把流"扁平化"**,让流元素的值合在一起成一个流,使用的flatMap方法,这里也是类似的,对象放在Optional容器里面,访问它的值(对象)内部的属性时,这个属性对应一个Optional<Optional<对象的属性所属的类>>,即Optional的值也是一个Optional,通过flatMap将它"扁平化"则变成一层Optional,即Optional<对象的属性所属的类>;
int age = p1. **flatMap **(TO_Person::getName).flatMap(TO_Person::getPersonByName).flatMap(TO_Person::getAge).orElse(0);
}
filter过滤
@Test
public void func_03(){
TO_Person person = new TO_Person("Lee", 20);
Optional<TO_Person> p1 = Optional.of(person);
int age = p1.flatMap(TO_Person::getName).flatMap(TO_Person::getPersonByName).flatMap(TO_Person::getAge).orElse(0);
System.out.println(age);
int age1 = p1.flatMap(TO_Person::getName).flatMap(TO_Person::getPersonByName).flatMap(TO_Person::getAge).filter(a -> a < 20).orElse(0); //过滤<20的age
System.out.println(age1);
}
Optional序列化
public final class Optional<T> {...}说明Optional没有实现java.io.Serlizable接口,不能序列化;
解决:提供一个方法getOptional:return Optional.ofNullable(name);
*从Optional中取值的方法
@Test
public void func_04() {
TO_Person person = new TO_Person("Lee", 20);
TO_Person person1 = null;
Optional<TO_Person> p = Optional.ofNullable(person);
System.out.println(p.get());//**(1)get方法从Optional容器中取值,但是最不安全,值为null会报错NoSuchElementException**
Optional<TO_Person> p1 = Optional.ofNullable(person1);
System.out.println(p1.orElse(new TO_Person("--", 0)));//**(2)orElse方法,如果值为null(Optional.empty())返回一个默认值(这个值是<u>事先初始化好的</u>);**
p1 = Optional.ofNullable(person1);
System.out.println(p1.orElseGet(() -> new TO_Person("--", 1)));//**(3)orElseGet方法,传入一个supplier,如果值为null(Optional.empty())返回一个默认值,是orElse方法的"延迟版",当null时才会调用Consumer;**
//System.out.println(p1.orElseThrow(()->new ArrayIndexOutOfBoundsException()));//**(4)orElseThrow,和get类似,在Optional值为空时候抛出指定Runtime异常;**
p.ifPresent(x-> System.out.println(p+" existed"));//**(5)ifPresent,当值存在时才去作为参数传入Consumer执行某个操作,值为null则无操作;**
p1.ifPresent(x-> System.out.println(" existed"));
}
避免使用基础类型与Optional
之前的Stream中,测试了装箱的性能损失,如IntegerStream与Stream<Integer>,性能损失的原因是大量的流元素进行装箱操作;
这里Optional内只放了一个值,不存在这种情况,不推荐使用基础类型的Optional(OptionalInt、OptionalLong、OptionalDouble)因为他们没有filter和flatMap方法;
小结:
- Optional是为解决对象参数为空值的一种容器;
- Optional的使用方法与Stream很像,包括map、filter、flatMap;
- 在返回值(如getter方法)里面尝试使用Optional;