为什么在lambda中使用的局部变量fi​​nal必须是final或有效的?

本文介绍了Java Lambda表达式中关于final局部变量的规则,强调了这一限制是为了防止并发问题。Lambda本质上是匿名函数,其对外部变量的访问通过构造器实现。值传递和引用传递的区别在示例中得以展示,说明了为何在并发场景下,确保变量不可变的重要性。同时,文章指出Lambda仅声明,不立即执行,进一步解释了并发问题的来源。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

直接的答案

JLS给我们了一些提示,它说对有效的最终变量的限制对动态变化的本地变量的访问,本地变量可能会引入并发问题。但是,这是什么英文呢?

简单的说:java为了防止修改数据不同步而定的,也就是防止你在lambda内部使用的外层局部变量被外层代码修改了,但是lambda内部无法同步。

使用final属性的属性,初始化后就不能修改,可以理解该属性读改。

Java提示必须制定final才行:

public static void main(String[] args) {         int[] nums = new int[]{1,2,4};         int num = 1;              //外部发生修改         num--;         nums = new int[]{1};         Thread thread= new Thread(()->{             nums[0] = 0;             int result = num -1;         });     }复制错误复制成功

lambda函数的本质

没有用过lambda表达式的,你应该也过匿名函数,lambda表达式用的本质就是一个匿名函数,再合理一点,一个函数式接口实现的实例。

是一个接口的实现,那么外部变量是如何传递的?

很条件是通过构造器

为什么不是普通方法和构造器?因为拉姆达是要说明参数的,但不能是任意的,它的参数列表需要依靠接口的构造器而定。

lambda表达式实例化的时候,编译器会创建一个新的类文件(想一下你是不是在工程编译之后公开Main$1.class的文件),该文件就是lambda实例化的类的字节码文件,在该文件中,编译器帮我们创建了一个构造器,该构造器的入参中就包含了你要使用的外层局部变量,所以外层局部变量就通过lambda的构造器实例实例内部供其使用。

值传递和引用传递

你是通过构造器传参,构造器也是方法,

如果这个变量是基本类型,那可能是一个变量传递,你那个变量层代码修改了了,那lambda 内也可能就在这个变量上,就可以找到外面的问题了。

这也是官方为了避免误会,这实际上是一个副本,不是原来的值

我外部一发生改变,匿名函数,lambda函数内就会报错:

public static void main(String[] args) {         int num = 1;         //外部发生修改         num--;         Thread thread= new Thread(()->{             int result = num - 1;    //报错         });     }复制错误复制成功

如果是引用传递,实际上并不存在这个问题了,因为finalkey只是维护引用的地址,而不会维护引用的对象内部的属性值,这样是可以的:

public static void main(String[] args) {         int[] nums = new int[]{1,2,4};         //外部发生修改         nums[0] = 0;     //    nums = new int[]{1}; 这样是会报错的,nums地址发生改变         Thread thread= new Thread(()->{             nums[0] = 1;         });     }复制错误复制成功

但如果nums这个地址发生了改变,仍然会报错,就和表面的值传递一个链接了。

lambda 只是声明,不代表执行

是lambda还是匿名内部类,在写lambda表达式的时候,是不会直接去执行这个lambda表达式的,lambda只是一种,声明变量一样,你声明一个int x;声明,可能在很多行代码之后去调用这个lambda表达式才执行

上面的例子继续讲:

public static void main(String[] args) {         int num = 1;         Thread thread= new Thread(()->{    //3             int result = num - 1;    //报错         });         thread.start();    //6         num -- ;    //7     }复制错误复制成功

这里的第3行是个lambda的声明,不能马上执行。

第6行启动线程,才是执行这个lambda

可能修改了第 7 行先执行,变成了把0你的lambda 内的值,然后把你的lambda 内的值1,这样导致了数据不同步的问题,这也解释了一个开头的说:变量可能会引入并发问题

 扫VX 领Java资料,前端,测试,python等等资料都有

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值