引言: 学完领悟java8新特性需要大概两三天,如果一天就学完可能啥也没记住
b站视频资源链接:关于java8的内容在列表最下面,大概三个多小时
为什么要学java8
java8(jdk1.8和java8是一个东西)对比于java7 新增了一些新语法特性 ,
自己开发用不用都可以,有时候可能还会会显得多此一举 , 有时候又很有用
主要是为了看懂别人的代码和为看懂源码排坑
接口增强
JDK 7 及以前只能定义全局常量和抽象方法
- 全局变量 public static final
- 抽象方法 public abstract
JDK 8 除了定义全局常量和抽象方法
还可以定义静态方法,默认方法
调用方式和普通方法一样(静态直接调 ,非静态需要new对象)如下
函数式接口
什么是函数式接口
如果一个接口中,只声明了一个抽象方法,就称该接口为函数式接口
如 Runnable接口只有run()方法 , Comparator接口只有accept()方法
可以加上@FunctionalInterface 标注一下
如何理解函数式接口
以及为什么要有函数式接口 : 面向函数编程
比如传统即使是静态方法,也需要先写在某个类里面,再调用;
函数式接口能让我们在需要这个函数的地方直接写这个函数,或直接在参数部分写这个函数,
比如(sort(Comparator) 需要什么方法就在括号里面写Comparator就是一个函数式接口)
java内置的四大函数式接口及案例
接下来的lanbda表达式
就是作为这些接口的实例化表达式使用,
对于需要以下四种类型的函数 直接调用函数式接口实现,没有重新写静态方法类的必要
传统写法写函数式接口: 定义需要传入方法的函数+匿名类实现方法
案例1: Consumer接口案例
定义:
传统写法如下:
接下来介绍的lambda方法方式:
案例2: predicate使用案例
public void Test8(){
List<String> strings = Arrays.asList("背景", "北京", "南京", "天津");
//传统写法 自定义predicate方法
List<String> all = fillString(strings, new Predicate<String>() {
@Override
public boolean test(String s) {
return s.contains("京");
}
});
System.out.println(all);
// lambda写法 自定义predicate方法
List<String> l2 = fillString(strings, s -> s.contains("京"));
System.out.println(l2);
}
//定义一个对list判断的方法
public List<String> fillString(List<String> list, Predicate<String> pre){
ArrayList<String> strings = new ArrayList<>();
for (String s : list) {
if(pre.test(s)){
strings.add(s);
}
}
return strings;
}
下方lambda表达式内容将提供更多结合应用案例
其他接口
需要再查,主要是为了看懂别人的代码,自己用不用无所谓
Lanbda表达式
快速介绍
就是把重写函数方法的方式——>化简
以下是传统方法写函数式接口 和 用lambda表达式写函数式接口
上述为实现Runable的匿名类中的一个run方法
可以写成 Lanbda 表达式的条件
- 该类只有一个方法(所有就不用方法名了,直接写参数和内部表达式) : ( ) -> 方法
使用(6种)
Runnable r2 = () -> System.out.println("我爱你.."); // 语法格式一: 无参 ,无返回
public void Test2(){
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer.accept("sdsd");
System.out.println("------------------------");
//调用格式二: Lambda 需要一个参数 ,但是没有返回值
//--------------------------------------------------------
Consumer<String> consumer1 = (String s) -> {
System.out.println(s);
};
//--------------------------------------------------------
consumer1.accept("SDASDSA");
}
public void Test3(){
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer.accept("sdsd");
//调用格式三: Lambda 数据类型只有一个参数时可以省略 ,因为可以由编译器推出,称为"数据推理"
//----------------------------------------------------------------
Consumer<String> consumer1 = (s) -> {//省略数据类型
System.out.println(s);
};
//----------------------------------------------------------------
consumer1.accept("SDASDSA");
}
public void Test4(){
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer.accept("sdsd");
//调用格式四: Lambda 若只有一个参数,参数的小括号可以省略
//----------------------------------------------------------------
Consumer<String> consumer1 = s -> {
System.out.println(s);
};
//----------------------------------------------------------------
consumer1.accept("SDASDSA");
}
public void Test5(){
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
};
System.out.println("------------------------------");
//调用格式五:Lambda 需要两个或两个以上的参数,多条执行语句,并且可以有返回值
//----------------------------------------------------------------
Comparator<Integer> com2 =(o1 ,o2) -> {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
//----------------------------------------------------------------
}
public void Test6(){
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
};
System.out.println("------------------------------");
//调用格式五:Lambda 只有一条语句时,return 和大括号都可以省略
//----------------------------------------------------------------
Comparator<Integer> com2 =(o1 ,o2) -> o1.compareTo(o2);
//----------------------------------------------------------------
}
本质
lambda表达式的本质: 作为函数式接口的实现
方法引用/构造器引用
什么是方法引用
图片内容为核心思想,看不懂继续往下看
简单来说就是 : 和所需要的lambda表达式:功能,参数类型,返回类型一样的方法已经有其他类写过了
,我就没必要重新写(有点方法可能很复杂,重写成本高),那么我就直接引用过来用就好了
什么时候能用方法引用
- 引用方法比较复杂的使用用
- 注意能够引用的方法必须和所需方法参数返回类型完全一致才能用
- 当然有的参数类型是泛型(就由例如 Consumer consumer1 = out :: println; 的String指定)
案例: 引用java的System.out.println演示
方法引用的三种方法
方法一: 对象 :: 非静态方法
//lambda写法
Consumer<String> consumer = str -> System.out.println(str);
consumer.accept("北京");
//引用写法 :先写对象或new对象 , 再用对象名引用非静态方法
PrintStream out = System.out;
Consumer<String> consumer1 = out :: println;//println()是非静态方法
consumer1.accept("北京");
方法二:类名 :: 静态方法
System.out.println("----------传统----------");
Function<Double,Long> function = new Function<Double, Long>() {
@Override
public Long apply(Double d) {
return Math.round(d);
}
};
function.apply(2.0);
System.out.println("----------lambda表达式-----------");
Function<Double,Long> function1 = d -> Math.round(d);
function1.apply(2.0);
System.out.println("----------方法引用静态方法/非静态方法-----------");
Function<Double,Long> function2 = Math :: round;
System.out.println(function2.apply(2.0));
方式三: 类:: 实例方法 (基本很少能想到用这个,属于是能用就用,不用也无所谓)
比较难理解的是: 如图片 compare(t1 ,t2) 和 t1.compare(t2) 会认为是可以1对1匹配等价的,
下图
test( t1,t2) 可以认为与 t1.equals(t2) 的t1 和 t2 匹配
(匹配指这样调用不会出现函数不一致的语法错误,仅此而已)
下图
Function的R对应getName()的返回值
T t 对应 getName() 的小括号的空值
一样可以匹配,不会语法错误
方式三还是不懂,那就能不用就不用吧,免得出问题
-
想不到方法三,想方法二,方法一
-
想不到三种方法,想lambda表达式
-
想不到lambda表达式,就用传统写法
构造器引用
依旧是上面的传统方法,lambda表达式,引用方法
调用无参构造器
调用有一个参数构造器
调用多个参数构造器
引用写法是一样的,自动识别, 语法记住就好
数组引用
意会——>和方法引用类似
但是我觉得没必要多此一举23333333
Stream API
介绍
为什么要用StreamAPI
nosql不能直接sql语法进行数据库操作 ,就需要通过java层面写增删改查函数
比如mysql数据库可以通过sql语法对表进行过滤挑选( 如 select * from table1 where age >10;
返回table1 中年龄大于10的数据) ,但是有的数据库不支持sql语法,需要在java层面对数据进行过滤,Stream就类似于用java的API实现sql语法的功能
只有执行终止操作才能生效(类似于sql语法的runback回滚)
终止操作是指中间对数据本身进行过滤的操作之后的所有操作( 如过滤完之后forEach()方法就是终止的方法之一)
创建Stream方法
方法一:通过集合
演示案例:
准备如下list数据进行演示
可自定义获取一些数据,如上方数据加入自定义的获取一个下方的employee的集合list
- 通过该employee集合对象调用Stream(): 可获取顺序流(能按加入的顺序取出)
- 通过该employee集合对象调用parallelStream():可获取并行流(可能有多个流同时取出,不一定按顺序)
方法二:通过数组
演示案例
方式三: 通过Stream的of()
演示案例
方式四:创建无限流(用得较少)
迭代(了解即可)
将返回结果作为新的输入参数,无限制循环(除非设限制如limit(10) : 限制10次)
可以用来造数据
以下打印效果
生成(了解即可)
可以用来造数据
如下生成十个随机数
Stream的中间操作
筛选与切片
只有如下四个方法是中间操作,其余都算终止操作
下方的list是一串员工数据employee包含Salary工资等信息
接收 :filter()
如下: 执行一次终止操作(如forEach())后必须重新用list.stream()生成一次Stream,否则报错
截断和跳过:limit() ,skip()
去重: distinct()
如下如果打印只会保留一个"刘强东";
映射
map(function)用法:
相当于将每个元素一个一个通过该function得到返回值,将返回值组成新序列
如果一个元素是stream()会将整个stream()放入
理解类比: list.add(1) ,
list.add(2),
list.add([3,4])
——> [ 1 , 2 , [ 3 , 4 ] ]
flatMap(): 如果遇到stream会把它拆开
理解类比:
list.add(1);
list.add(2),
list.addAll([3,4])
——>[ 1 , 2, 3 ,4 ]
排序
演示案例:
Stream的终止操作
1.匹配和查找
allMatch(Predicate p) :是否全部满足p
anyMatch(Predicate p): 是否存在满足p
noneMatch(Predicate p): 是否没有满足p
firstFirst: 返回第一个
count(): 数数
max(Comparator p) : 按规则排序,拿最大
min(Comparator p): 按规则排序,拿最小
forEach(Comparator c): 迭代遍历 ,以下forEach属于一个stream 一个Colleate
2.规约
map进行映射,reduce进行规约——>举例(map求出每个工资元素,reduce进求sum)
T iden: 指设置的初始值
BinaryOperator : 指接收两个参数,返回一个结果的函数类型
如下案例初始值为10 ,再把1~10加起来 返回65给sum
求工资总和案例
不会写引用就用lambda表达式
3.收集
将流Stream转化为其他形式如:集合Set ,Map,list
方法: 注意红色几个用到较多,其他需要再查,可做了解
案例:查找工资>6000的收集为集合list
Optional类
为什么有Optional类
Optional可以通过检测空值的方式,避免空指针异常( 空指针异常指: 对象为空还用该对象调方法,从而报错)
Optional的方法
创建方法:
optional()
ofUullable()
没有optional时,我们只能向如下代码一样手动判断是否为空( 以下图片代码帮助理解手动判断,意思不要深究)
用optional代替手动判断if(null)的写法案例如下:
- 构造成optional对象
- 调用orElse(T)方法: 对象不空,就用该对象; 该对象为null,就用orElse(T)的备胎T
- 若有返回对象,用get()将对象从optional获取
:比如通过optional获取原对象: 原对象不能为空 ,或用 判断方法isPresent()判断
其他对应调用方法
新的时间和日期API
属于java基础内容,学过java基础应该都知道, 就是 localdateTime() 处理时间那些方法,此处不多说
其他新特性
java8新特性比较特别特别的的就是以上
函数式接口,lambda表达式,Stream API 和 Optional类这些
其他的特性可能变成了java基础,不太明显