Lambda表达式是特殊的匿名内部类,语法更简洁
2.为什么使用Lambda表达式匿名内部类
public class Test1 {
public static void main(String[] args) {
Runnable runnable=new Runnable() {
@Override
public void run() {
System.out.println("今天你开心了吗?");
}
};
Thread t1=new Thread(runnable);
t1.start();
}
static class fun implements Runnable{
@Override
public void run() {
System.out.println("===========");
}
}
}
-
Thread 类需要 Runnable 接口作为参数,其中的抽象 run 方法是用来指定线程任务内容的核心
-
为了指定 run 的方法体,不得不需要 Runnable 接口的实现类
-
为了省去定义一个 Runnable 实现类的麻烦,不得不使用匿名内部类
-
必须覆盖重写抽象 run 方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错
-
而实际上,似乎只有方法体才是关键所在。
-
我们可以使用Lambda表达式
2.Lambda表达式的使用<函数式接口><变量名>=(参数1,参数2)->{
方法体
}
注意: 函数式接口:接口中只有一个抽象方法。
(参数1,参数2): 抽象方法的参数
->: 分隔符
{}:表示抽象方法的实现
public class Test {
public static void main(String[] args) {
//简化匿名内部类的使用,语法更加简单
//使用Lambda表达式
Runnable runnable=()->{
System.out.println("话梅排骨");
};
//创建线程对象
Thread thread=new Thread(runnable);
//开启线程
thread.start();
}
}
3.Lamnda表达式可以把函数当做一个方法的参数(函数作为方法参数传递),将代码像数据一样传递
Thread thread=new Thread(()->{
System.out.println("话梅排骨");
});
//开启线程
thread.start();
4.无参无返回值的Lambda表达
public static void main(String[] args) {
//使用Lambda表达式
show(()->{
System.out.println("我想去游泳");
});
}
//静态方法
public static void show(swimming s){
s.swimming();
}
//自己定义一个函数式接口
interface swimming{
//抽象函数
public void swimming();
}
5.有参有返回值的Lambda
下面举例演示 java.util.Comparator 接口的使用场景代码,其中的抽象方法定义为:
-
public abstract int compare(T o1, T o2);
当需要对一个对象集合进行排序时, Collections.sort 方法需要一个 Comparator 接口实例来指定排序的规则。
package guan.demo2;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Test {
public void main(String[] args) {
//创建集合
List people=new ArrayList<>();
//添加数据
people.add(new Person("李雷",20));
people.add(new Person("李化",30));
//对集合中的元素进行排序 按照年龄从大到小。
//传统做法:Comparator: 排序规则接口
//int: 0表示新加的元素和集合中原来的比对的相同
//1: o2比o1小
//-1: o2比o1大
Collections.sort(people,(o1,o2)->{
return o2.getAge()-o1.getAge();
});
for (Person p:people) {
System.out.println(p);
}
}
//定义一个类
class Person{
private String name;
private int age;
@Override
public String toString() {
return "Person{" +
"name='" + name + ''' +
", age=" + age +
'}';
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
}
}
6.详细介绍Lambda表达式
Lamdba引入了新的操作符:->(箭头操作符)->将表达式分为两部分
左侧:(参数1,参数2.....)表示参数列表
有参:{}内部是方法体
注意事项:
1.形参列表的数据类型会自动推断
2.如果形参列表为空,只需要保留()
3.如果形参列表只有一个,()可以省略,只需要参数的名称
4.如果执行语句只有一句,且没有返回值,{}可以省略,若有返回值,则若想去省略{}就必须同时省略return,并且执行语句也保证只有语句
5.Lambda不会生成一个单独的内部类文件
4.函数式接口 1.什么是函数式接口如果一个接口只有一个抽象方法,则该接口称之为函数式接口,函数式接口可以使用Lambda表达式会被匹配到这个抽象方法上。
@Functionallnterface注解检查接口是否符合函数式接口
2.内置函数式接口的由来 public static void main(String[] args) {
fun(arr -> {
int sum=0;
for(int n:arr){
sum+=n;
}
System.out.println("数组的和为:"+sum);
});
}
public static void fun(Operater operater){
int[] arr={2,3,4,5,6,7,11};
operater.getSum(arr);
}
}
@FunctionalInterface
interface Operater{
//求数组的和
public abstract void getSum(int[] arr);
}
分析
我们知道使用Lambda表达式的前提是需要有函数式接口。而Lambda使用时不关心接口名,抽象方法名,只关心抽 象方法的参数列表和返回值类型。因此为了让我们使用Lambda方便,JDK提供了大量常用的函数式接口。
3.常见的函数式接口--java.util.function 1.comsumer public static void main(String[] args) {
fun((name)->{
System.out.println("我的姓名"+name);
},"张三");
}
public static void fun(Consumer consumer,String name){
//Consumer是jdk提供的,不需要我们自己定义函数式接口
consumer.accept(name);
}
2.supplier public static void main(String[] args) {
//new Random().nextInt(10)随机产生随机数0-10之间
sum(()->new Random().nextInt(10));
}
public static void sum(Supplier supplier){
//supplier.get();的返回类型是泛型
Integer result = supplier.get();
System.out.println(result);
}
3.Function public static void main(String[] args) {
//函数的长度length()
//toUpperCase()将小写转为大写
fun((msg)-> msg.length()
//msg传进去的字符串
,"你好,想吃饭吗?");
}
//Function(参数类型的泛型,函数返回结果的泛型)
public static void fun(Function function, String msg){
Integer apply = function.apply(msg);
System.out.println("长度为"+apply);
}
4.Predicatedpublic static void main(String[] args) {
//判断名字的长度是否长
fun(name->name.length()>3?true:false,"迪丽热巴");
}
public static void fun(Predicate predicate,String name){
boolean test = predicate.test(name);
System.out.println("名字长吗?"+test);
}
5.方法引用
1.什么是方法引用
方法引用是Lambda表达式的一中简写形式。如果Lambda表达式方法体中只是调用一个特定的已经存在的方法,则可以使用方法引用
2.为什么使用方法的引用public class Test01 {
public static void main(String[] args) {
Consumer c=arr->{
int sum=0;
for(int b:arr){
sum+=b;
}
System.out.println("数组的和为:"+sum);
};
fun(c);
}
public static void fun(Consumer consumer){
Integer[] arr={1,2,3,4,5};
consumer.accept(arr);
}
public static void sum(Integer[] arr){
int sum=0;
for (int a:arr){
sum+=a;
}
System.out.println("数组的和为:"+sum);
}
}
思考:如果我们在Lambda中所指定的功能,已经有其他方法存在相同方案,那是否还有必要再写重复逻辑?可以直接“引 用”过去就好了:---方法引用
3.使用方法引用public class Test1 {
public static void main(String[] args) {
//方法调用
fun(Test1::sum);
}
//有参无返回值
public static void fun(Consumer consumer){
Integer[] arr={2,3,4,5,6,7,8,9};
consumer.accept(arr);
}
//sum里面的参数类型要和fun里面的参数类型相匹配
public static void sum(Integer[] arr){
int sum=0;
for(int a:arr){
sum+=a;
}
System.out.println("和"+sum);
}
}
4.方法引用的常见形式
1. 对象::实例方法
2.类::静态方法
3.类::实例方法
4.类::new
5.方法引用的分类 1.静态方法引用静态方法引用,Lambda表达式要满足=(参数,..)->类名.静态方法(参数);可以简写为=类名::静态方法
public static void main(String[] args) {
//静态方法调用
List list=new ArrayList<>();
list.add(3);
list.add(7);
list.add(6);
list.add(9);
//进行排序
//静态方法
list.sort(Integer::compare);
System.out.println(list);
}
2.实例方法引用
实例方法引用,顾名思义就是调用已经存在的实例的方法,与静态方法引用不同的是类要先实例化,静态方法引用类无需实例化,直接用类名去调用。
实例方法引用,Lambda表达式要满足=(参数)->实例对象.实例方法(参数)
public static void main(String[] args) {
//创建对象
User user=new User("李四",12);
//在Lambda表达式中有且只有一条语句,并且这条语句又调用了某个方法
// Supplier s=()->{
// return user.getName();
// };
// Supplier s=user::getName;
fun(user::getName);
}
public static void fun(Supplier supplier){
//供给型--无参有返回值
String s = supplier.get();
System.out.println(s);
}
}
class User{
public String name;
public int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
3.对象方法引用
对象方法引用: 类名::实例方法. (参数1,参数2)->参数1.实例方法(参数2)
package guan.demo4;
import java.util.function.BiFunction;
import java.util.function.Function;
public class Test3 {
public static void main(String[] args) {
//对象方法
// BiFunction b=(str1,str2)->{
// return str2.equals(str2);
// };
//BiFunction双重函数里面有三个参数第一个参数是参数1的泛型,第二个参数是参数2的泛型,第三个参数是返回结果的泛型
BiFunction b=String::equals;
Boolean apply = b.apply("你好", "你好");
System.out.println(apply);
}
}
class People{
public String name;
public People() {
}
@Override
public String toString() {
return "People{" +
"name='" + name + ''' +
'}';
}
public People(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
4.构造方法引用
构造方法引用: 类名::new (参数)->new 类名(参数)
/构造方法
Function function=(name)->{
return new People(name);
};
Function function=People::new;
People people = function.apply("李四");
System.out.println(people);
6.stream流
Java8的两个重大改变,一个是Lambda表达式,另一个就是本节要讲的Stream API表达式。==Stream 是Java8中处理集合的关键抽象概念==,它可以对集合进行非常复杂的查找、过滤、筛选等操作.
1.什么是stream流Stream流式思想类似于工厂车间的“生产流水线”,Stream流不是一种数据结构,不保存数据,而是对数据进行加工 处理。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。
注意:stream流只是加工处理数据,不保存数据
2.为什么使用stream流public class Test {
public static void main(String[] args) {
// 一个ArrayList集合中存储有以下数据:张无忌,周芷若,赵敏,张强,张三丰
// 需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据
ArrayList list = new ArrayList<>();
Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰");
// 1.拿到所有姓张的
ArrayList zhangList = new ArrayList<>(); // {"张无忌", "张强", "张三丰"}
for (String name : list) {
if (name.startsWith("张")) {
zhangList.add(name);
}
}
// 2.拿到名字长度为3个字的
ArrayList threeList = new ArrayList<>(); // {"张无忌", "张三丰"}
for (String name : zhangList) {
if (name.length() == 3) {
threeList.add(name);
}
}
// 3.打印这些数据
for (String name : threeList) {
System.out.println(name);
}
}
}
思考:这段代码中含有三个循环,每一个作用不同:
1.首先筛选所有姓张的人;
2.然后筛选名字有三个字的人;
3.最后进行对结果进行打印输出。
每当我们需要对集合中的元素进行操作的时候,总是需要进行循环、循环、再循环。这是理所当然的么?不是。循环 是做事情的方式,而不是目的。每个需求都要循环一次,还要搞一个新集合来装数据,如果希望再次遍历,只能再使 用另一个循环从头开始。 --使用stream
3.使用stream流public class Test {
public static void main(String[] args) {
ArrayList list = new ArrayList<>();
Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰","何线程");
//使用stream()流
//调用stream流
//filter(需要断言型)--过滤器
//startsWith以什么开始
//获取流
list.stream()
.filter(item->item.startsWith("张"))
.forEach(item-> System.out.println(item));
}
}
对集合的操作语法简洁:性能比传统快。
4.如何获取stream流对象 1.通过Collection(集合)对象的stream()或者parallelStream()方法 //创建一个集合
List list=new ArrayList();
list.add("你好");
list.add("我好");
list.add("大家好");
//获取stream流
Stream stream = list.stream();
//list.parallelStream串行
2.通过Arrays(数组)类的stream()方法
//创建一个数组
String[] array={"广州好迪","滴答滴答","哗啦哗啦"};
//获取stream流
Stream stream1 = Arrays.stream(array);
3.通过stream接口的of(),iterate(),generate()方法
Stream4.通过intstream,longstream,doublestream接口中的of,range,rangeclosed方法stream2 = Stream.of(2, 3, 4, 5, 6, 9);
//打印的话是1-19--rang不包括右边
LongStream range = LongStream.range(1, 20);
//打印1-20--rangClosed包括右边
LongStream.rangeClosed(1,20).forEach(System.out::println);
5.stream流中常见的api
中间操作api: 一个操作的中间链,对数据源的数据进行操作。而这种操作的返回类型还是一个Stream对象。
终止操作api: 一个终止操作,执行中间操作链,并产生结果,返回类型不在是Stream流对象。
注意:无论执行多少中间操作,只要没有执行终止操作,中间操作就不会执行
1.中间操作filter--过滤器 //filter(需要断言型)--过滤器
//startsWith以什么开始
//获取流
list.stream()
.filter(item->item.startsWith("张"))
.forEach(item-> System.out.println(item));
}
2.map
map--接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
//根据map--名字最长的 --map里面是一个函数型接口--有参有返回值
personList.stream()
.sorted((o1,o2)->o1.getAge()-o2.getAge())
.forEach(System.out::println);
//即需要name有需要age
personList.stream()
.map(item->{
Map m=new HashMap<>();
m.put("name",item.getName());
m.put("age",item.getAge());
return m;
})
.forEach(System.out::println);
3.sorted
//根据年龄排序--sorted
personList.stream().sorted(((o1, o2) -> o1.getAge()- o2.getAge())).forEach(System.out::println);
4.终端操作collect--收集成为一个新的集合或者其他
//收集 -女生并且国籍是中国的
List collect = personList.stream()
.filter(item -> item.getCountry().equals("中国"))
.filter(item -> item.getSex() == 'F')
.collect(Collectors.toList());
System.out.println(collect);
5.foreach
5.find,match.forEach(System.out::println);
find查找
//findFirst 查找满足条件的第一个
Optional first = personList.stream()
.filter(item -> item.getCountry() == "中国")
.findFirst();
System.out.println(first.get());
//findAny匹配任意一个,适合并行流
Optional any = personList.parallelStream()
.filter(item -> item.getSex() == 'F')
.findAny();
System.out.println(any.get());
match匹配
//anyMatch--满足其中一个返回true--要求括号里面的是一个断言型的
//allMath--所有的元素都满足返回true
//nonelMath--没有一个人符合返回true
boolean b = transactions.stream()
.map(ietm -> ietm.getTrader())
.anyMatch(item -> item.getCity() == "Milan");
System.out.println(b);
6.reduce规约
规约,也称缩减,顾名思义,是把一个流缩减成一个值,能实现对集合求和,求乘积和求最值操作
//求年龄的和--参数和返回类型必须一致
Optional reduce = personList.stream()
.map(item -> item.getAge())
.reduce((o1, o2) -> o1 + o2);
System.out.println(reduce.get());
//reduce: 18,24->18+24->42 ,42,22->44+22-66
Integer reduce = personList.stream()
.map(item -> item.getAge())
.reduce(10, (a, b) -> a + b);
System.out.println(reduce);
//10+(a+b)的值
7.聚合 min max count
count--可以统计人数
long l = personList.stream()
.filter(item -> item.getCountry().equals("中国"))
.count();
System.out.println(l);
max--最大值
Optionalmax = transactions.stream() .max((o1, o2) -> o1.getValue() - o2.getValue()); System.out.println(max.get());
min--最小值
Optional7.新增的时间日期类 1.旧的时间日期的缺点 1.设计比较乱: Date日期在java.util和java.sql也有,而且它的时间格式转换类在java.text包。 2.线程不安全。 2.新增的时间日期类有哪些 1.LocalDate: 表示日期类。格式默认:yyyy-MM-ddmin = transactions.stream() .min((o1, o2) -> o1.getValue() - o2.getValue()); System.out.println(min.get());
//获取当前的时间
LocalDate now = LocalDate.now();
// System.out.println(now);
//指定日期
LocalDate of = LocalDate.of(2000, 07, 12);
// System.out.println(of);
2.LocalTime: 表示时间类。格式默认: HH:mm:ss
//获取当前时间
LocalTime now1 = LocalTime.now();
System.out.println(now1);
//指定时间
LocalTime of1 = LocalTime.of(9, 33, 54);
System.out.println(of1);
3.LocalDateTime: 表示日期时间类 格式默认: yyyy-MM-dd t HH:mm:ss sss
//获取当前日期时间
LocalDateTime now2 = LocalDateTime.now();
System.out.println(now2);
//指定日期时间
LocalDateTime of2 = LocalDateTime.of(of, of1);
System.out.println(of2);
LocalDateTime of3 = LocalDateTime.of(2021, 9, 20, 10, 04, 33);
System.out.println(of3);
4.DatetimeFormatter:日期时间格式转换类。
//需要转为什么样的格式
DateTimeFormatter dateTimeFormatter=DateTimeFormatter.ofPattern("yyyy-MM-dd");
//将字符串按照dateTimeFormatter的格式转换
LocalDate parse = LocalDate.parse("1999-11-12", dateTimeFormatter);
System.out.println(parse);
//将日期转变为字符串
String format = parse.format(dateTimeFormatter);
System.out.println(format);
5.Instant: 时间戳类。
//获取到当前的时间戳
Instant now3 = Instant.now();
System.out.println(now3);
//获取时间戳的数值 时间戳数值是从1970-01-01 00:00:00开始到当前时间所经过的秒数
long epochSecond = now3.getEpochSecond();
System.out.println(epochSecond);
6.Duration: 用于计算两个日期时间类
Duration between = Duration.between(of3,now2);
//相差的天数
System.out.println(between.toDays());



