Java8
Oracle JDK是基于Open JDK源代码的商业版本,要学习Java新技术可以去OpenJDK官网学习。
Lambda表达式介绍
-
匿名内部类存在的问题
-
new Thread(new Runnable() { @Override public void run() { // ... } }).start();
-
匿名内部类做了哪些事
- 定义了一个没有名字的类
- 实现了Runnable接口
- 创建了这个类的对象
-
其实我们最关注的是run方法和里面要执行的代码,使用匿名内部类语法是很冗余的
-
Lambda体现的是函数式编程思想,只需要将要执行的代码放到函数中
-
Lambda就是一个匿名函数,我们只需要将要执行的代码放到Lambda表达式中即可
-
-
体验Lambda表达式
-
new Thread(() -> { //... }).start();
-
好处:
- 简化匿名内部类,让代码更加精简
-
Lambda的标准格式
-
Lambda表达式是一个匿名函数,而函数相当于Java中的方法
-
(参数列表) -> { }
-
():参数列表
-
{}:方法体
-
->:没有实际含义,起到连接的作用
-
-
小结:
- 以后看到方法的参数是接口就可以考虑使用Lambda表达式
- 可以这么认为:Lambda表达式就是对接口中抽象方法的重写
- 不是所有匿名内部类都能用Lambda来替换
Lambda的实现原理
- 匿名内部类在编译的时候会形成一个.class文件
- Lambda在程序运行时会形成一个类
- 在类中新增一个方法,这个方法的方法体就是Lambda表达式中的代码
- 还会形成一个匿名内部类,实现接口,重写抽象方法
- 在接口的重写方法中会调用新生成的方法
- 在类中新增一个方法,这个方法的方法体就是Lambda表达式中的代码
- 理解:Lambda表达式就是对抽象方法的重写
Lambda省略格式
// 省略前
(int a) -> {
return new Person();
}
// 省略后
a -> new Person()
- 小括号内参数的类型可以省略
- 小括号内有且仅有一个参数,小括号可以省略
- 大括号内有且仅有一个语句,可以同时省略大括号、return关键字及分号
Lambda的前提条件
- 方法的参数或局部变量的类型必须为接口才能使用Lambda
- 接口中有且只有一个抽象方法
- 只有一个抽象方法的接口称为函数式接口,我们就能使用Lambda
- @FunctionalInterface:检测这个接口是否只有一个抽象方法
Lambda和匿名内部类对比
- 所需的类型不一样
- 匿名内部类:需要的类型可以是类、抽象类、接口
- Lambda:需要的类型必须是接口
- 抽象方法的数量不一样
- 匿名内部类:数量随意
- Lambda:只能有一个
- 实现原理不同
- 匿名内部类:编译后形成class文件
- Lambda:运行时动态生成class
JDK8接口新增的两个方法
-
JDK8以前
-
interface 接口名 { 静态常量; 抽象方法; }
-
-
JDK8对接口的增强,接口还可以有默认方法和静态方法
-
JDK8
-
interface 接口名 { 静态常量; 抽象方法; 默认方法; 静态方法; }
-
-
默认方法
-
- 因此JDK8时为接口新增了默认方法
- 接口中的默认方法实现类不必重写,可以直接使用,实现类也可以根据需要重写
-
默认方法的定义格式
-
interface 接口名 { 修饰符 default 返回值类型 方法名() { //... } }
-
-
默认方法的使用
- 实现类直接调用接口默认方法
- 实现类重写接口默认方法
-
-
静态方法
-
为了方便接口扩展,JDK8为接口新增了静态方法
-
静态方法的定义格式
-
interface 接口名 { 修饰符 static 返回值类型 方法名() { //... } }
-
-
静态方法的使用
- 接口静态方法实现类不会继承、不能重写?
- 使用:接口名.静态方法名()
-
-
区别
- 默认方法通过实例调用,静态方法通过接口名调用
- 默认方法可以被继承,实现类可以直接使用接口默认方法,也可以重写接口默认方法
- 静态方法不能被继承,实现类不能重写接口静态方法,只能通过接口名调用
常用内置函数式接口
-
在 java.util.function包中
-
Supplier接口
-
public interface Supplier<T> { T get(); }
-
-
Consumer接口
-
public interface Consumer<T> { void accept(T t); }
-
-
Function接口
-
public interface Function<T, R> { R apply(T t); }
-
-
Predicate接口
-
public interface Predicate<T> { boolean test(T t); }
-
方法引用格式
- 符号表示: ::
- 符号说明:双冒号为方法引用运算符,而它所在表达式被称为方法引用
- 应用场景:如果Lambda所要实现的方案,已经有其他方法存在相同方案,那么则可以使用方法引用
对象名::引用成员方法
- 注意事项
- 被引用的方法,参数要和接口中抽象方法的参数一样
- 当接口的抽象方法有返回值时,被引用的方法也必须有返回值
类名::引用静态方法
类名::引用实例方法
- 注意:
- 实际上会将第一个参数作为方法的调用者
- 类名:: 中的类名为第一个参数的类
类名::new引用构造器
数组::new 引用数组构造器
Stream流式思想概述
- 与IO流没有任何关系
获取Stream的两种方式
-
根据Collection获取流
-
Collection接口中有一个默认方法:default Stream
stream() -
Stream<String> stream1 = list.stream(); Stream<String> stream2 = set.stream(); Stream<String> stream3 = map.keySet().stream(); Stream<String> stream4 = map.values().stream(); Stream<Map.Entry<String, String>> stream5 = map.entrySet().stream();
-
-
Stream中静态方法of获取流
-
public static<T> Stream<T> of(T... values) { return Arrays.stream(values); }
-
Stream<String> stream6 = Stream.of("aa", "bb", "cc"); String strs = {"aa", "bb", "cc"}; Stream<String> stream7 = Stream.of(strs); //基本数据类型不行,会将整个数组看作一个元素操作 int[] arr = {11, 22, 33}; Stream<int []> stream8 = Stream.of(arr);
-
Stream常用方法和注意事项
-
终结方法
- 返回值不再是Stream的方法
-
非终结方法(函数拼接方法)
- 返回Stream
-
注意事项
- Stream只能操作一次
- Stream方法返回的是新的流
- Stream不调用终结方法,中间的操作不会执行
-
forEach
-
count
-
filter:返回值为true要,不是true不要
-
limit
-
skip:跳过前几个数据
-
map:映射,将一种类型的流转换为另一种类型
-
sorted
-
distinct:过滤重复数据
- 自定义类型,重写hashCode和equils
-
match:是否匹配指定条件
- allMatch:匹配所有元素,所有元素都需要满足条件
- anyMatch:匹配某个元素,只要有其中一个元素满足条件即可
- noneMatch:匹配所有元素,所有元素都不满足条件
-
find
- findFirst
- findAny
-
max和min
- 指定排序规则
- max取排序后最后一个值
- min取排序后第一个值
-
reduce:将数据归纳总结得到一个数据
-
T reduce(T identity, BinaryOperator<T> accumulator); // T identity: 默认值 // BinaryOperator<T> accumulator: 对数据进行处理的方式
-
int reduce = Stream.of(4, 5, 3, 9).reduce(0, (x, y) -> x + y);
- 第一次,将某人之赋值给x,取出集合第一元素赋值给y
- 第二次,将上次返回的结果赋值x,取出下个元素赋值给y
-
可以用于获取最大值
-
(0, (x, y) -> { return x > y ? x : y; })
-
-