一、Lambda是什么?
Lambda表达式是Java8推出的语法糖,用于极简简化【函数式接口】的匿名内部类代码。
传统匿名内部类代码冗余、模板代码多,Lambda可以去掉冗余格式,只保留参数 + 核心逻辑,也是 Stream流、函数式编程的底层基础。
二、必备前提:函数式接口
1. 定义
满足以下条件的接口,就是函数式接口:
有且仅有一个抽象方法(可以有多个默认方法、静态方法)
2. 专属注解
@FunctionalInterface
加在接口上,用于强制校验接口是否为函数式接口,编译报错提示,规范代码。
3. 自定义函数式接口示例
@FunctionalInterface
interface MyInterface {
// 1个抽象方法 *必须唯一
void test();
// 默认方法:随便写,不算抽象方法
default void demo1(){}
default void demo2(){}
// 静态方法:随便写,不算抽象方法
static void static1(){}
static void static2(){}
}
三、Lambda标准语法 & 四种简化规则
1. 标准完整语法
(参数列表) -> { 方法体逻辑 }
-
(参数列表):对应抽象方法的参数
-
->:Lambda固定箭头符号,连接参数和方法体
-
{}:方法执行的核心代码
2.四层极简简化规则
所有简化可叠加使用,最终代码极度精简
参数类型可省略:编译器可自动推断参数类型,无需手动声明
单个参数可省略小括号:参数只有一个时,() 可以不写
单行方法体可省略大括号:方法体只有一行代码,无需 {} 包裹
单行返回可省略return:唯一一行代码是return返回值时,可省略return和{}
四、Lambda的三种调用方式
方式一:先定义变量,再调用
逻辑:把Lambda表达式赋值给接口变量 → 再调用接口的抽象方法
特点:直观、新手友好、可复用
@FunctionalInterface
interface Study {
void learn(String subject);
}
// 1. 定义Lambda,赋值给接口变量
Study s3 = sub -> System.out.println("正在学习:" + sub);
// 2. 调用接口方法,执行Lambda逻辑
s3.learn("Lambda表达式");
执行顺序:先存逻辑 → 后执行逻辑
方式二:匿名对象直接传(不用接收变量)
逻辑:不需要定义变量,直接把Lambda当作方法参数传入,用完即走
特点:代码更短、一次性使用、Stream底层全是这种写法
@FunctionalInterface
interface Study {
void learn(String subject);
}
// 写一个方法,接收函数式接口参数
public static void test(Study study){
study.learn("直接传入的Lambda");
}
public static void main(String[] args) {
// 直接把Lambda传进去,不定义变量
test(sub -> System.out.println("正在学习:" + sub));
}
//输出:
//正在学习:直接传入的Lambda
核心理解:Lambda本质就是一段可传递的代码逻辑,可以直接当参数传输
方式三:最简实战(Stream经典写法)
这是你之后写代码用得最多的方式,本质就是方式二的进阶版
List<String> list = Arrays.asList("Java","MySQL","Lambda");
// 直接传入Lambda,无变量、无多余代码
list.stream().forEach(str -> System.out.println("学习:" + str));
原理:forEach方法接收Consumer函数式接口,直接传入Lambda实现逻辑
五、Java内置四大核心函数式接口
开发、Stream流高频使用,无需自定义接口,直接调用
1. Consumer< T > 消费型接口
特点:有参、无返回值,用于消费、处理数据
常用场景:forEach遍历数据
Consumer<String> consumer = str -> System.out.println("输出:" + str);
consumer.accept("测试数据");
2. Supplier< T > 供给型接口
特点:无参、有返回值,用于生产、获取数据
Supplier<Integer> supplier = () -> (int)(Math.random()*100);
System.out.println("随机数:" + supplier.get());
3. Function<T,R> 函数型接口
特点:有参、有返回值,用于数据转换、类型映射
常用场景:Stream map字段提取、数据转换
Function<String,Integer> function = str -> str.length();
System.out.println("字符串长度:" + function.apply("Lambda"));
4. Predicate< T > 断言型接口
特点:有参、返回布尔值,用于条件判断、数据筛选
常用场景:Stream filter过滤数据
Predicate<Integer> predicate = num -> num > 10;
System.out.println(predicate.test(15)); // true
System.out.println(predicate.test(5)); // false
六、Lambda终极简化:方法引用
当Lambda方法体仅调用一个已有方法,无额外逻辑,可使用方法引用进一步简化
// 普通Lambda写法
list.stream().forEach(s -> System.out.println(s));
// 方法引用最简写法
list.stream().forEach(System.out::println);
七、Lambda核心禁忌与注意事项
Lambda表达式不是独立代码块,有严格的使用限制
1. 仅限函数式接口使用(硬性前提)
Lambda的核心作用是简化单个抽象方法的函数式接口,若接口包含两个及以上抽象方法,无法使用Lambda表达式,编译器直接报错。
原理:Lambda无法精准匹配多个抽象方法,不知道需要重写哪一个方法。
2. 外部局部变量隐式Final规则(超级重点)
一句话大白话结论:Lambda 只能读取【不变的外部局部变量】,不能读取被改过的变量
详细规则:
-
Lambda 内部可以访问、读取 方法中的局部变量
-
但是!这个局部变量 必须是没被修改过的(Java隐式认定为 final)
-
只要你对外部局部变量做二次赋值,Lambda 直接报错
通俗理解:
Lambda 是延迟执行的代码,Java怕你外面改了变量,Lambda里面的值混乱,所以强制规定:给Lambda用的局部变量,必须固定不变。
正确示例(能运行):变量没改过
public static void main(String[] args) {
// 只赋值一次,没修改,隐式final
String info = "我是固定数据";
// Lambda可以正常使用
test(() -> System.out.println(info));
}
错误示例(直接报错):变量被修改了
public static void main(String[] args) {
String info = "我是固定数据";
// 二次修改!破坏隐式final规则
info = "被修改了";
// 报错!Lambda不能用被修改的局部变量
test(() -> System.out.println(info));
}
3. 内部不支持break、continue关键字
Lambda属于独立的函数式代码块,不属于循环体内部,无法使用循环跳转关键字 break、continue,强行编写会直接报错。
如需终止遍历、跳过元素,需使用Stream流的 filter、limit 等专属方法实现。
4. 无法直接抛出受检异常
Lambda表达式不允许直接抛出编译期受检异常(Exception、IOException等),如果代码逻辑存在异常风险,必须手动 try-catch 捕获处理,不能直接throws抛出。
5. 无独立作用域,this指向特殊
Lambda没有自己的局部作用域,内部的 this、super 均指向外部所属类对象,而非Lambda自身,不存在匿名内部类的this重写歧义,这是Lambda与匿名内部类的核心区别之一。
6. 不能单独声明,必须绑定函数式接口
Lambda是一段匿名代码逻辑,不是变量、不是方法,无法单独存在,必须赋值给函数式接口变量、或直接传入函数式接口参数,否则语法报错。
7. 禁止在Lambda内修改原集合数据
遍历集合时,Lambda内部禁止增删、修改原集合元素,会触发并发修改异常(ConcurrentModificationException),也是Stream编程的核心编码规范。
-
只能作用于函数式接口:多抽象方法接口绝对不能用Lambda
-
变量隐式final规则:Lambda内部使用的外部局部变量,默认隐式final,不能二次赋值修改
-
无break/continue:Lambda表达式内部不支持这两个关键字
-
不能抛出受检异常:如需异常处理,必须手动try-catch
-
无this、super重写歧义:Lambda没有自己的作用域,this指向外部类对象
八、Lambda与Stream的关系
-
Stream流的所有中间操作、终端操作(filter、map、forEach等),参数都是内置函数式接口
-
Lambda是Stream的语法基础,没有Lambda,Stream代码会极其繁琐
-
二者搭配使用,彻底告别for循环遍历、处理集合数据







