Java基础全套教程(十一)—— 函数式编程详解
Java基础全套教程(十一)—— 函数式编程详解
前言
JDK8 是 Java 语言发展史上极具里程碑意义的版本,其中最重要的两大更新就是Lambda表达式与Stream流式编程,二者共同构成了 Java 的函数式编程体系。
在 JDK8 之前,Java 是纯粹的面向对象编程语言,所有代码都必须依托类与对象实现,即使是简单的逻辑执行,也需要通过匿名内部类、重载方法等繁琐方式编写,代码冗余度极高。而函数式编程的引入,让 Java 兼具了面向对象与函数式编程的优势,允许将代码逻辑(方法)作为参数、变量、返回值传递,极大简化了集合操作、逻辑回调、流式数据处理的代码,让代码更简洁、优雅、高效。
本节课将从零详解函数式编程核心思想、Lambda表达式、函数式接口、方法引用、变量捕获、内置核心函数接口以及 Stream 流式编程,全程搭配全新原创实操案例,无冗余习题、无老旧复刻代码,适配零基础学习与进阶开发。
一、函数式编程核心思想
1.1 什么是函数式编程
编程范式主要分为面向对象编程、函数式编程、命令式编程等。
面向对象编程:核心思想是“万物皆对象”,通过封装对象、调用对象方法完成业务逻辑,关注谁来做。
函数式编程:核心思想是“函数即数据”,将方法(函数)作为一等公民,可以独立定义、赋值、传参、返回,关注做什么,弱化对象的束缚,专注逻辑本身。
1.2 Java函数式编程的核心价值
代码极简:彻底替代臃肿的匿名内部类,一行代码实现复杂逻辑。
逻辑清晰:链式编程风格,数据处理流程一目了然,告别嵌套冗余代码。
支持流式处理:结合Stream流,快速实现集合过滤、排序、映射、统计等复杂操作。
便于并行计算:Stream原生支持并行流,轻松实现多线程数据处理,提升效率。
二、Lambda表达式核心基础
2.1 Lambda表达式简介
Lambda表达式是 JDK8 推出的函数式编程核心语法,是对匿名内部类的极简优化,专门用于简化函数式接口的实例化与方法实现。
传统Java语法中,无法将一段代码逻辑赋值给变量;而Lambda表达式实现了这一能力,它可以将一段动态代码块封装为一个“函数对象”,赋值给函数式接口变量,实现逻辑的动态传递。
2.2 函数式接口(Lambda的前提)
函数式接口是Lambda表达式唯一的使用载体,没有函数式接口,Lambda表达式无法使用。
2.2.1 定义规范
满足以下条件的接口即为函数式接口:接口中有且仅有一个抽象方法。
核心注意点:接口中可以包含多个默认方法(default修饰)、静态方法、私有方法,这些非抽象方法不影响函数式接口的判定。
2.2.2 @FunctionalInterface注解
该注解是函数式接口的标识与校验注解,作用如下:
显式声明当前接口为函数式接口;
编译期强制校验,接口中一旦出现多个抽象方法,直接编译报错,避免接口失效;
仅为编译校验,即使不添加该注解,满足单抽象方法的接口依然是函数式接口。
2.2.3 自定义函数式接口案例
// 无参无返回值函数式接口@FunctionalInterfacepublicinterfaceAction{voidexecute();}// 单参数无返回值函数式接口@FunctionalInterfacepublicinterfaceConsumerTask{voidaccept(Stringcontent);}// 多参数有返回值函数式接口@FunctionalInterfacepublicinterfaceComputeTask{intcalculate(intnum1,intnum2);}2.3 Lambda表达式标准语法
2.3.1 完整语法结构
Lambda表达式固定结构:(参数列表) -> { 方法体代码 }
(参数列表):对应函数式接口抽象方法的参数列表,无参则空括号,多参逗号分隔;
->:Lambda专属运算符(读作 goes to),固定写法,连接参数与方法体;
{}:方法体,对应抽象方法的具体实现逻辑。
2.3.2 Lambda五大语法特性(可省略规则)
Lambda的核心优势就是语法精简,满足条件可省略冗余代码,所有省略规则均为编译器自动推断:
参数类型可省略:参数类型可全部省略,编译器根据接口抽象方法自动推断;
单参数括号可省略:参数列表仅有一个参数时,外层小括号可以省略;
无参数括号不可省:没有参数时,必须保留空括号;
单行方法体大括号可省略:方法体只有一行执行代码,可省略大括号与分号;
单行返回值可省略return:方法体仅有一行返回逻辑,可省略return关键字与大括号。
三、Lambda表达式完整实操与语法简化
基于上面自定义的三类函数式接口,从零演示完整写法与极简写法,全覆盖无参、单参、多参、无返回、有返回场景。
3.1 无参无返回值场景
publicclassLambdaDemo1{publicstaticvoidmain(String[]args){// 完整写法Actionaction1=()->{System.out.println("执行无参无返回Lambda逻辑");};action1.execute();// 极简写法(单行代码,省略大括号、分号)Actionaction2=()->System.out.println("极简无参Lambda执行");action2.execute();}}3.2 单参数无返回值场景
publicclassLambdaDemo2{publicstaticvoidmain(String[]args){// 完整写法ConsumerTasktask1=(Stringmsg)->{System.out.println("接收参数:"+msg);};task1.accept("Java函数式编程");// 极简写法(省略参数类型、括号、大括号)ConsumerTasktask2=msg->System.out.println("极简接收参数:"+msg);task2.accept("Lambda表达式");}}3.3 多参数有返回值场景
publicclassLambdaDemo3{publicstaticvoidmain(String[]args){// 完整写法ComputeTasktask1=(inta,intb)->{returna*b;};System.out.println("完整写法乘积:"+task1.calculate(6,8));// 极简写法(省略参数类型、return、大括号)ComputeTasktask2=(a,b)->a*b;System.out.println("极简写法乘积:"+task2.calculate(9,9));}}四、方法引用(Lambda进阶简化)
当Lambda表达式的方法体仅仅是调用一个已存在的成熟方法,没有额外逻辑时,可以使用方法引用进一步简化代码,替代冗余的Lambda写法,让代码更加简洁优雅。
核心要求:被引用方法的参数列表、返回值类型必须与函数式接口抽象方法完全一致。
4.1 方法引用语法与分类
通用语法:方法归属者::方法名
静态方法引用:类名::静态方法名
实例方法引用:对象实例::普通方法名
4.2 实操案例
publicclassMethodRefDemo{// 静态方法:匹配双参数int返回值接口publicstaticintgetSum(intx,inty){returnx+y;}// 普通实例方法:匹配单参数无返回值接口publicvoidprintStr(Stringstr){System.out.println("打印内容:"+str);}publicstaticvoidmain(String[]args){// 1.静态方法引用(替代Lambda:(a,b) -> getSum(a,b))ComputeTasksumTask=MethodRefDemo::getSum;System.out.println("两数之和:"+sumTask.calculate(15,25));// 2.实例方法引用MethodRefDemodemo=newMethodRefDemo();ConsumerTaskprintTask=demo::printStr;printTask.accept("方法引用简化Lambda");}}五、Lambda变量捕获(闭包特性)
5.1 闭包核心概念
闭包是指能够访问外部作用域变量的代码块。在Java中,Lambda表达式、匿名内部类都属于闭包实现,可以访问方法内的局部变量。
5.2 变量捕获规则
Lambda表达式访问外部局部变量时,变量会被隐式修饰为final,无需手动添加final关键字,但不允许二次赋值,否则编译报错。这也是Lambda闭包的核心特性:捕获的变量不可变。
5.3 实操案例
publicclassLambdaClosureDemo{publicstaticvoidmain(String[]args){// 被Lambda捕获的局部变量,隐式finalStringtips="函数式编程闭包特性";intcount=10;Actionaction=()->{// 可以读取捕获的外部变量System.out.println("捕获变量:"+tips);System.out.println("计数常量:"+count);// 报错:变量不可二次赋值// count = 20;};action.execute();}}六、JDK内置四大核心函数式接口
日常开发中无需频繁自定义函数式接口,JDK8 内置了大量通用函数式接口,其中四大核心接口覆盖90%的开发场景,是Stream流开发的基础。
6.1 Consumer消费型接口
核心特点:有参、无返回值,用于消费处理数据,核心方法void accept(T t)。
常用场景:集合遍历、数据打印、数据消费处理。
importjava.util.ArrayList;importjava.util.List;importjava.util.function.Consumer;publicclassConsumerDemo{publicstaticvoidmain(String[]args){List<String>nameList=newArrayList<>();nameList.add("张三");nameList.add("李四");nameList.add("王五");// Lambda实现消费遍历Consumer<String>consumer=name->System.out.println("用户姓名:"+name);nameList.forEach(consumer);}}6.2 Predicate断言型接口
核心特点:有参、返回布尔值,用于数据条件判断、过滤,核心方法boolean test(T t)。
常用场景:集合过滤、条件筛选、数据校验。
importjava.util.ArrayList;importjava.util.List;importjava.util.function.Predicate;publicclassPredicateDemo{publicstaticvoidmain(String[]args){List<Integer>numList=newArrayList<>();numList.add(12);numList.add(25);numList.add(36);numList.add(48);// 筛选大于20的数字Predicate<Integer>predicate=num->num>20;numList.removeIf(predicate.negate());// 取反:删除不满足条件的System.out.println("筛选后数据:"+numList);}}6.3 Function函数型接口
核心特点:有参、有返回值,用于数据类型转换、数据处理映射,核心方法R apply(T t)。
常用场景:类型转换、数据加工、字段映射。
importjava.util.function.Function;publicclassFunctionDemo{publicstaticvoidmain(String[]args){// 字符串转整数Function<String,Integer>function=str->Integer.parseInt(str);Integernum=function.apply("666");System.out.println("转换后的数字:"+num);}}6.4 Supplier供给型接口
核心特点:无参、有返回值,用于对外提供数据,核心方法T get()。
常用场景:数据生成、对象创建、默认值供给。
importjava.util.function.Supplier;importjava.util.Random;publicclassSupplierDemo{publicstaticvoidmain(String[]args){// 随机生成100以内整数Supplier<Integer>supplier=()->newRandom().nextInt(100);System.out.println("随机数:"+supplier.get());}}七、Stream流式编程详解
Stream流是JDK8基于Lambda表达式推出的集合数据处理工具,专门用于简化集合、数组的复杂数据操作,支持过滤、排序、映射、统计、去重、限制等链式操作,代码极简、可读性极强。
核心特性:Stream流不会修改原数据源,所有操作都会生成新的数据流,支持链式调用。
7.1 Stream流操作三阶段
获取流(创建阶段):通过集合、数组、静态方法生成Stream流;
中间操作(处理阶段):对流中数据进行过滤、排序、映射等处理,返回新流,可链式叠加;
终结操作(结果阶段):终止流操作,生成最终结果,执行后流关闭,不可复用。
7.2 Stream流常用中间操作
7.2.1 filter 数据过滤
基于Predicate接口,根据条件筛选数据,保留符合条件的元素。
importjava.util.ArrayList;importjava.util.List;importjava.util.stream.Collectors;publicclassStreamFilterDemo{publicstaticvoidmain(String[]args){List<String>fruitList=newArrayList<>();fruitList.add("苹果");fruitList.add("香蕉");fruitList.add("牛油果");fruitList.add("芒果");// 筛选名字长度为3的水果List<String>result=fruitList.stream().filter(fruit->fruit.length()==3).collect(Collectors.toList());System.out.println("筛选结果:"+result);}}7.2.2 sorted 数据排序
支持自然排序、自定义排序,简化集合排序逻辑。
importjava.util.ArrayList;importjava.util.List;importjava.util.stream.Collectors;publicclassStreamSortDemo{publicstaticvoidmain(String[]args){List<Integer>numList=newArrayList<>();numList.add(22);numList.add(5);numList.add(89);numList.add(36);// 降序排序List<Integer>sortList=numList.stream().sorted((n1,n2)->n2-n1).collect(Collectors.toList());System.out.println("降序排序:"+sortList);}}7.2.3 limit、skip 数据截取
limit(n):截取前n个元素;skip(n):跳过前n个元素,常用于分页场景。
importjava.util.ArrayList;importjava.util.List;publicclassStreamLimitDemo{publicstaticvoidmain(String[]args){List<Integer>numList=List.of(1,2,3,4,5,6,7,8,9,10);// 跳过前2个,截取3个元素numList.stream().skip(2).limit(3).forEach(System.out::println);}}7.2.4 map 数据映射转换
基于Function接口,将流中元素转换为新的数据类型,实现数据加工。
importjava.util.List;importjava.util.stream.Collectors;publicclassStreamMapDemo{publicstaticvoidmain(String[]args){List<String>strList=List.of("10","20","30","40");// 字符串转整数,并翻倍List<Integer>intList=strList.stream().map(Integer::parseInt).map(num->num*2).collect(Collectors.toList());System.out.println("转换加工后:"+intList);}}7.3 Stream常用终结操作
常用终结方法:forEach()遍历、collect()收集集合、count()统计数量、max/min()最值、anyMatch()匹配判断。
八、本章核心总结
函数式编程核心是将方法作为数据,Lambda表达式是Java函数式编程的核心语法;
Lambda表达式必须依托函数式接口(单抽象方法接口)使用,@FunctionalInterface用于编译校验;
Lambda语法支持多层精简,单行代码可省略括号、大括号、return等冗余语法;
方法引用是Lambda的进阶简化,用于直接调用已有成熟方法,代码更简洁;
Lambda闭包会隐式固化外部局部变量,变量不可二次修改;
JDK四大核心函数式接口:Consumer消费型、Predicate断言型、Function函数型、Supplier供给型;
Stream流基于Lambda实现,通过创建、中间操作、终结操作三步,极简完成集合复杂数据处理,不修改原数据。
