开门见山的说啊,这是java为了防止数据不一样步而规定的,也就是防止你在lambda内使用的外层局部变量被外层代码修改了,可是lambda内部没法同步这个修改java
那么问题就转移到,为何你在外层代码修改了变量,lambda内部会感知不到呢?这个问题就要回到lambda表达式的本质了函数
lambda表达式的本质是什么?也就是匿名内部类,或者再精确一点,就是一个函数式接口的实现的实例嘛code
那再想一下,你把外层局部变量拿到这个实例去使用,这个变量是如何传进去的?是经过普通方法传参吗?固然不是,你参数列表又不能改变,答案是构造器嘛。lambda表达式实例化的时候,编译器会建立一个新的class文件(想一下你是否是在工程编译以后见到过相似于Main$1.class的文件),该文件就是lambda实例化的类的字节码文件,在该文件中,编译器帮咱们建立了一个构造器,该构造器的入参中就包含了你要使用的外层局部变量,因此外层局部变量就经过lambda的构造器传入实例内部供其使用对象
那么这个时候就很好理解,为何lambda内使用的外层局部变量必须是final了。你想嘛,既然你是经过构造器传参,构造器也是方法,若是这个变量是基本类型,那确定是值传递嘛,也就是传副本儿,那既然是副本,你在外层代码修改了这个变量,那lambda内确定就没法感知了嘛。不过啊,若是是引用传递,其实就不存在这个问题了,由于final关键字只是维护引用的地址,而不会维护引用的对象内部的属性值接口
最后要说的一点就是,不管是lambda仍是匿名内部类,在写lambda表达式的时候,是不会直接去执行这个lambda表达式的,lambda只是一种声明,和声明变量同样,你声明一个int x;仅仅是声明,可能在不少行代码以后才去调用这个lambda表达式的执行,例如:编译器
Thread thread = new Thread(() -> System.out.println("call")); System.out.println("main call"); thread.start();
执行第1行代码,声明了lambda表达式,但此时不会执行lambda表达式
执行第3行代码,才会去执行lambda同步
好了如今就很清晰了,假如你在第1行代码的lambda内使用了一个外层局部变量,此时其值为10,而后在第2行代码改变了这个变量为12,最后第3行代码去执行lambda,但其内部这个变量的值仍然为10,这就形成了数据不一样步的问题编译