Java8 学习笔记,PPT 备忘录~
  
 
  
 
  
 
  
 
默认方法让接口 增加新方法 的同时又能保证对使用这个接口的 老版本代码的兼容
  
 
如果在面向接口编程里面,功能 1 要新增一个方法,在接口中添加了该方法,则实现该接口的其他类都得再去实现这个方法
所以在 Java8 中新增了接口默认方法,默认方法让接口 增加新方法 的同时又能保证对使用这个接口的 老版本代码的兼容
  
 
 假设 gif 中的 Exoplayer 、 Exoplayer Wrapper 和 app 是分别三个团队,当 app 发现一个 bug 后一层层往上报,最终发现是 Exoplayer 团队的 bug,那么 Exoplayer 团队将 bug fix 了,发布了 Exoplayer 2.0 : 
Exoplayer 2.0 不是二进行兼容的,那么 Exoplayer Wrapper 也需要重新编译发布 2.0 版本 Exoplayer 2.0 是二进行兼容的,那么 Exoplayer Wrapper 无需重新编译, app 直接引用 Exoplayer 2.0 边行 接口的修改是二进制兼容的,但是如果这样修改了的话会让程序出现不可控的异常
  
 
  
 
default
一个类只能 继承一个 父类,可以 实现多个 接口
但是加了默认方法之后,在默认方法的使用上出现了一些冲突
  
 
default
Optional是 Java 8 提供的为了解决 null 安全问题的一个 API
def path = mManagerData?.getData()?.getInfo()?.getPath() : getDefaultPath();
 Java 8 为啥不引入 安全操作符 呢?先来看看 Optional : 
public String getPath() {
    return Optional.ofNullable(mManagerData)
            .map(ManagerData::getData)
            .map(Data::getInfo)
            .map(Info::getPath).orElse(getDefaultPath());
}
 
   整个样式结构跟 Stream 很相似,当然搭配着 Stream 食用味道更佳 
public Optional<ManagerData> getFirstDraftData() {
    LinkedHashMap<Long, String> linkedHashMap = mDB.getFirst();
    if (linkedHashMap.size() != 1) {
        return Optional.empty();
    }
    ArrayList<ManagerData> dataList = new ArrayList<>();
    ManagerData data = null;
    for (Map.Entry<Long, String> entry : linkedHashMap.entrySet()) {
        long id = entry.getKey();
        String content = entry.getValue();
        getDirtyOrCleanDraft(dataList, id, content);
        if (dataList.size() > 0) {
            data = dataList.get(0);
        }
    }
    return Optional.of(data);
}
 
   如果是对外提供功能,返回值是一个 Optional 的话更能让调用者知道该怎么操作 
public String getPath() {
    return Optional.ofNullable(mManagerData)
            .map(ManagerData::getData)
            .map(Data::getInfo)
            .map(Info::getPath).orElseGet(() -> getDefaultPath())
}
 
   orElse 方法的延时调用版 orElesGet :如果是 orElse 默认情况下会先去初始化失败情况下的值,如果是 orElesGet 的话,只有失败了的情况才会走其中的方法 
用更简洁流畅的代码完成一个功能
public static List<Apple> filterGreenApples(List<Apple> list) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : list) {
        if ("green".equals(apple.color)) {
            result.add(apple);
        }
    }
    return result;
}
 
  第一个版本,需要将绿颜色的苹果给挑选出来
public static List<Apple> filterColorApples(List<Apple> list, String color) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : list) {
        if (color.equals(apple.color)) {
            result.add(apple);
        }
    }
    return result;
}
 
  经过第一个版本后,需求改了,颜色可能是各种各样的,那么 将颜色作为参数传进来 的方式来满足需求
####版本 3
public static List<Apple> filterColorOrWeightApples(List<Apple> list, String color, int weight) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : list) {
        if (color.equals(apple.color) || weight > apple.weight) {
            result.add(apple);
        }
    }
    return result;
}
 
  又经过一个版本,判断条件可能不止颜色,还有重量,那么再将重量作为参数传进来;但是这样的做法会 显得越来越笨拙
public static List<Apple> filter(List<Apple> list, Filter filter) {
    List<Apple> result = new ArrayList<>();
    for (Apple apple : list) {
        if (filter.satisfied(apple)) {
            result.add(apple);
        }
    }
    return result;
}
interface Filter {
    boolean satisfied(Apple apple);
}
 
  那么优化一下,通过类似策略模式的方式,放入不同的算法来实现
class WeightFilter implements Filter {
    @Override
    public boolean satisfied(Apple apple) {
        return apple.weight > 100;
    }
}
class ColorFilter implements Filter {
    @Override
    public boolean satisfied(Apple apple) {
        return "red".equals(apple.color);
    }
}
list = filter(list, new WeightFilter());
list = filter(list, new ColorFilter());
 
  这样就把行为抽象出来了,代码适应了需求的变化,但是这个过程很 啰嗦 ,因为需要声明很多只需要实例化一次的类
list = filter(list, new Filter() {
   
    @Override
    public boolean satisfied(Apple apple) {
        return apple.weight > 100;
    }
});
 
  再优化一下上个版本的问题,改为用匿名内部类,虽然上个版本的问题解决了,但是匿名内部类看起来很笨重,占用空间多,除此之外,有时候看起来还特别费力
int weight = 100;
list = filter(list, new Filter() {
    int weight = 200;
    @Override
    public boolean satisfied(Apple apple) {
        return apple.weight > weight;
    }
});
 
   此时的 weight 看起来就比较费力,到底引用的是外部的还是内部的呢 
list = filter(list, apple -> apple.weight > 100);
通过 lambda 的方式,再来解决匿名内部类带来的问题
  
 
  
 
只定义了一个抽象方法的接口
@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
 
   如果再往 Runnable 中加方法,会导致编译失败 
  
 
Stream 可以更好的、更为流畅的、更为语义化的操作集合
Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念。Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。通常编写并行代码很难而且容易出错, 但使用 Stream API 无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序。所以说,Java 8 中首次出现的 java.util.stream 是一个函数式语言 + 多核时代综合影响的产物。
  
 
流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
数据操作又可以分为无状态的 (Stateless) 和有状态的 (Stateful) ,无状态中间操作是指元素的处理不受前面元素的影响,而有状态的中间操作必须等到所有元素处理之后才知道最终结果,比如排序是有状态操作,在读取所有元素之前并不能确定排序结果
终端操作又可以分为短路操作和非短路操作,短路操作是指不用处理全部元素就可以返回结果,比如找到第一个满足条件的元素
结构图,栗子中要用到
class Dish {
    String type;//小吃,素菜,荤菜
    String name;//名字
    int price;//钱
}
 
  从菜单中挑选出 荤菜 且 菜名带『肉』字 且 价格最贵的 3 个
List<String> filter(List<Dish> list) {
    List<Dish> typeResult = new ArrayList();
    for (Dish dish : list) {
        if ("荤菜".equals(dish.type)) {
            typeResult.add(dish);
        }
    }
    List<Dish> meatResult = new ArrayList<>();
    for (Dish dish : typeResult) {
        if (dish.name.contains("肉")) {
            meatResult.add(dish);
        }
    }
    Collections.sort(meatResult, new Comparator<Dish>() {
        @Override
        public int compare(Dish o1, Dish o2) {
            return o1.price - o2.price;
        }
    });
    List<String> nameList = new ArrayList<>();
    int count = 3;
    if (meatResult.size() < 3) {
        count = meatResult.size();
    }
    for (int i = 0; i < count; i++) {
        nameList.add(meatResult.get(i).name);
    }
    return nameList;
}
 
  List<String> filter2(List<Dish> list) {
    return list.stream().filter(dish -> "荤菜".equals(dish.type))
            .filter(dish -> dish.name.contains("肉"))
            .sorted(Comparator.comparingInt(o -> o.price))
            .limit(3)
            .map(dish -> dish.name)
            .collect(Collectors.toList());
}
 
    
 
  
 
  
 
  
 
List<String> filter2(List<Dish> list) {
    return list.stream().filter(dish -> "荤菜".equals(dish.type))
            .filter(dish -> dish.name.contains("肉"))
            .sorted(Comparator.comparingInt(o -> o.price))
            .limit(3)
            .map(dish -> dish.name)
            .collect(Collectors.toList());
}
 
  像 SQL,像 builder 构建者模式
  
 
集合是一种数据结构;它的主要关注点是在内存中组织数据,而且集合会在一段时间内持久存在。集合通常可用作流管道的来源或目标,但流的关注点是计算,而不是数据;每个中间操作都返回流,让整个流能串起来
  
 
自动并行是采用的 Fork / Join 框架
  
 
把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果
  
 
线程 1 负责处理 4 个任务,线程 2 负责处理 4 个任务,当线程 1 任务处理完了,但线程 2 还在处理任务。干完活的线程与其闲着,不如去帮其他线程干活。于是它就去其他线程的队里里窃取一个任务来执行。在这时它们会访问同一个队列,所以为了减少窃取任务线程和被窃取任务之间的竞争,通过会使用双端队列,被窃取任务线程永远从双端队列的头部执行任务,而窃取任务线程用于从双端队列的尾部拿任务。