一、default关键字
1、简述概念
default关键字:默认方法与冲突
在Java中有一条亘久不变的定理:类只能单继承,接口可以多实现。
Java8之前由于接口中的方法全部都是抽象方法,一个类如果同时实现两个拥有相同方法签名的接口并不会出现冲突,
实现类只需要重写该方法即可。但是在Java8中出现了默认方法,
这样一来就有可能会出现Java一直在避免的多继承问题——
一个类从多个地方(类或接口)继承了有相同方法签名的方法。
比如接口A有个方法和接口B的方法一样。一个实现类C实现了A和B,C的子类是D:
D调方法的优先级:C最大。
2、冲突
遇到这种情况时,我们需要遵循下面三条原则解决冲突:
一、类中方法优先级最高。类或父类中定义方法的优先级高于任何声明为默认方法的优先级。
二、第一条原则无法进行判断,子接口中声明的默认方法的优先级仅次于类中声明方法的优先级。
三、上述两条原则仍然无法判断,实现类必须显式重写方法或选择使用哪一个默认方法的实现。
3、jdk8的新特性与default的关联
JAVA8接口的新特性:
8之前只能定义常量和抽象方法。不能定义有方法体的方法。
8开始就可以定义
1、常量和抽象方法
2、静态方法,即static修饰的有具体实现的方法
3、default修饰的有具体实现的方法。
注意:static final修饰的变量还是不可以定义的。
4、进而联想default与Stream流的关系
这样改的原因,很可能是因为8的新特性 Stream 流 。
对集合而言,每种集合都需要有获取Stream对象的方法——stream()。
按照设计理念stream()方法应该在集合根接口Collection中声明,
然后让Collection的实现类重写该方法。
然而集合体系很庞大,
如果直接在Collection接口中声明stream()方法,
那么所有的Collection实现类都要重写该方法,
对现有的架构改动极大。
所以如果Collection接口中可以定义有具体实现的stream()方法就不会破坏现有的架构,
Collection的实现类只需要直接继承过来就可以了。
在JDK8的源码中,我们可以看到Collection接口中定义有default修饰的默认方法stream():
二、Stream
1、简述
Stream是Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API对集合数据进行操作,并且这个类很多方法的参数都是函数式接口。
2、步骤
Stream操作的三个步骤:
创建stream
中间操作(过滤、map)
终止操作
3、三大步骤的代码示例与讲解:
整个代码可以直接占到开发工具进行测试,根据注释进行进一步理解:
public class StreamTest1 {
public static void main(String[] args) {
List<Person> list= Arrays.asList(
new Person("1",11,5000),
new Person("2",21,65000),
new Person("3",31,1200),
new Person("6",61,26000),
new Person("4",24,25800),
new Person("4",41,25800)
);
//一、 创建stream
// 1,校验通过Collection 系列集合提供的stream()或者paralleStream()
Stream<Person> stream1 = list.stream();
Stream<Person> stream11 = list.stream();
// 2.通过Arrays的静态方法stream()获取数组流
String[] str = {"ss","23d","112","ss"};
Stream<String> stream2 = Arrays.stream(str);
// 3.通过Stream类中的静态方法of
Stream<String> stream3 = Stream.of("aa","bb","cc","bb","dd");
// 4.创建无限流
// 迭代
Stream<Integer> stream4 = Stream.iterate(0,(x) -> x+2);
//生成
Stream<Double> stream5 = Stream.generate(() -> Math.random());
//二、中间使用
//1、筛选和切片
//filter-接收Lambda,从流中排除某些元素。
stream1.filter(person -> person.getAge() > 1).forEach(System.out::println);
//stream1.limit(2).forEach(System.out::println);//报错,因为流中的信息只能操作一次,在进行终止操作后无法再次读取
//limit-截断流, 使其元素不超过给定数量(取前n个元素)。
stream2.limit(3).forEach(System.out::println);
//skip(n) -返回一个扔掉了前n个元素的流(取第n个元素后面的元素)。若流中元素不足n个,则返回一个空流。与limit(n)互补
//stream3.skip(3).forEach(System.out::println);
//stream3.collect(Collectors.toList());//报错,forEach属于终止操作
List<String> collect = stream3.skip(3).collect(Collectors.toList());
//distinct-筛选排重, 通过流所生成元素的hashCode()和equals()去除重复元素
// (如果想对实体类进行排重就需要实体类重写hashCode和equals这俩方法,
// 否则哪个方法没重写,那个方法就会根据空间做吧比较得到不相等的结果而无法排重)
stream11.distinct().forEach(System.out::println);
//2、映射 Stream.flatMap,正如它的名称所猜测的,是map和一个flat行动。这意味着您首先对元素应用一个函数,然后将其扁平化。Stream.map只对流应用函数,而不对流进行平坦处理。
//为了理解什么是扁平化,考虑一个像[[1,2,3],[4,5,6],[7,8,9]]这样的具有“两个层次”的结构。 扁平化意味着将其转化为“一个一级”结构:[1,2,3,4,5,6,7,8,9]。
//map-接收Lambda ,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,
// 并将其映射成一个新的元素。map的参数必须是Function,即传入一个参数返回一个参数
//比如将集合中的每个元素传进去获取每个元素的年龄
//map方法必须是一个参数返回一个参数,如果每个元素拆成数组,那也是多个数组.直接打印的话只能打印出每个数组的地址
list.stream().map(p-> String.valueOf(p.getAge()).split("")).forEach(System.out::println);//[Ljava.lang.String;@3d646c37
list.stream().map(p-> String.valueOf(p.getAge()).split("")).forEach(a->{
System.out.print("[");
for (int i = 0; i < a.length; i++) {
if (i==a.length-1) System.out.print(a[i]);
else System.out.print(a[i]+",");
}
System.out.print("] ");
});//[1,1] [2,1] [3,1] [6,1] [2,4] [4,1]
//flatMap-接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。最后所有的小元素都会汇集到一起在一个集合中
System.out.println();//1 1 2 1 3 1 6 1 2 4 4 1
list.stream().flatMap(p-> Stream.of(String.valueOf(p.getAge()).split(""))).forEach(a->{
System.out.print(a+" ");
});
//3、排序
String[] str2 = {"ss","23d","112","ss"};
//list.stream().sorted().forEach(System.out::println);//报错:org.Person cannot be cast to java.lang.Comparable
//sorted()-自然排序(Comparable)
Arrays.stream(str2).sorted().forEach(System.out::println);
//sorted(Comparator com)- 定制排序(Comparator) 比如根据工资从小到大排
list.stream().sorted((p,p2)-> p.getSalary()>=p2.getSalary()?0:-1).forEach(System.out::println);
//三、终止操作:查询、匹配、foreach,toArray,collect,求和reduce
//1、查找与匹配
//allMatch-检查是否匹配所有元素--所有元素都为true才为true
System.out.println(list.stream().allMatch((a) -> a.getAge() > 18));//false
//anyMatch-检查 是否至少匹配一个元素--只要有一个返回true即为true
System.out.println(list.stream().anyMatch((a) -> a.getAge() < 18));//true
//noneMatch- -检查 是否所有元素都没有匹配-- 所有元素都不满足返回false,最后结果才为true
System.out.println(list.stream().noneMatch((a) -> a.getAge() < 10));//false
//findFirst-返回第一个元素
System.out.println(list.stream().findFirst());
//findAny-返图当前流中的任意元素--非高并发情况返回第一个
System.out.println(list.stream().findAny());
//2、计算
//count-返回流中元素的总个数
System.out.println(list.stream().count());
//如果比较的结果为0则返回a1
//max-返回流中最大值 里面的比较函数结果为正数的数(与其他数比)
System.out.println(list.stream().max((a1,a2)-> a1.getAge()>=a2.getAge()?1:-1));
System.out.println(list.stream().max(Comparator.comparingInt(Person::getAge)));
//min-返回流中最个值 里面的比较函数结果为负数的数(与其他数比)
System.out.println(list.stream().min(Comparator.comparingInt(Person::getAge)));
//reduce-归纳,将流中元素反复按函数式参数(比如下面的(a,b)-> Long.sum(a, b))操作结合得到的结果
System.out.println(LongStream.rangeClosed(0, 100000000L).reduce(0,(a,b)-> Long.sum(a, b)));
//四、串并行流
//ForkJoin框架的简化版使用:使用 parallel(并行)/sequential(顺序串行流)来实现串行流和并行流
//list.stream().map(a->a.getAge()).sequential().reduce(0,(a,b)-> Integer.sum(a, b));方法引用简化如下:
Instant now = Instant.now();
System.out.println(list.stream().map(a -> a.getAge()).sequential().reduce(0, Integer::sum)+"耗时"+Duration.between(now,Instant.now()));
Instant now2 = Instant.now();
System.out.println(list.stream().map(a -> a.getAge()).parallel().reduce(0, Integer::sum)+"耗时"+Duration.between(now2,Instant.now()));
//最终耗时:串行是0.001s,并行是0.003s
//因为数量较少,而并行需要开多线程再合并结果,因此会比串行慢。因此并行更适合大数量的计算
//源码分析:java.util.stream.ReferencePipeline.reduce(P_OUT, java.util.function.BinaryOperator<P_OUT>)
//-->java.util.stream.AbstractPipeline.evaluate(java.util.stream.TerminalOp<E_OUT,R>)
//-->每个流的方法在返回时,都会判断是并行还是串行
/**
* isParallel()//是不是并行(默认不是并行)list.stream().map(a -> a.getAge()).reduce(0, Integer::sum) 这里没有指定就按默认的串行流走
* ? terminalOp.evaluateParallel(this, sourceSpliterator(terminalOp.getOpFlags()))
* : terminalOp.evaluateSequential(this, sourceSpliterator(terminalOp.getOpFlags()));
*/
//如果是并行,则创建ForkJoinTask的子类:对应方法的任务类,比如这个ReduceTask
//java.util.stream.ReduceOps.ReduceOp.evaluateParallel方法
//return new ReduceTask<>(this, helper, spliterator).invoke().get();
//如果是串行,则通过管道Sink<P_IN> wrappedSink负责开启和关闭流,中间通过遍历和划分源元素的对象Spliterator进行以此处理管道中的信息。
/**
* wrappedSink.begin(spliterator.getExactSizeIfKnown());
* spliterator.forEachRemaining(wrappedSink);
* wrappedSink.end();
*/
}
}
class Person{
String name;
int age;
int salary;
public Person(){
System.out.println("无参构造执行");
}
public Person(String name, int age, int salary) {
this.name = name;
this.age = age;
this.salary = salary;
System.out.println("有参构造执行");
}
public Person(int age){
this.age=age;
System.out.println("单参构造Function");
}
public Person(Person person) {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
salary == person.salary &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age, salary);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", salary=" + salary +
'}';
}
}