问题展示代码:
public static void test01() {
String str = "str";
new Thread(()->{
str += "yes";
System.out.println(str);
}).start();
}
在jdk1.8下, 在lambda表达式中访问str, 编译器未报错; 提示我不可访问非final的局部变量;这是我对str值改变的情况下; 当我们不改变str的值, 去访问str时, 却不会报错, 如下代码:
public static void test01() {
String str = "str";
new Thread(()->{
System.out.println(str);
}).start();
}
不对啊, 我的str并没有修饰成final啊, 只是未修改它的值而已, 为什么这次不会报错了呢? 其实吧! str此时的str就是一个final修饰的变量啊, 因为从头到尾, str并没有发生过任何修改, 它就是一个final;
我们对程序稍作更改:
public static void test01() {
String str = "str";
new Thread(()->{
System.out.println(str);
}).start();
str = "zhang";
}
我们main方法的尾部处, 将str的值改变了, 现在又开始编译错误了, 这说明str失去了final的特性了;
所以关于lambda不能访问非final修饰的变量的定义应该改为, lambda不能访问无final特性的局部变量;
为什么lamda不能访问非final特性的局部变量呢?
都知道lambda只是匿名内部类实现的一种简化写法, lambda会创造一个实现类且返回一个实现类的实例;
仔细分析一下这个程序:
public static void test01() {
String str = "china";
new Thread(()->{
System.out.println(str);
}).start();
str = "zhang";
}
我们可以认为test01这个方法为主线程, new Thread会新建一个线程并进行执行. 线程的执行实际是并发的, 这段程序并不能保证完完全全是按照主线程test01去顺序执行的. 此时主线程中我将str的值改为了"zhang",
那子线程如果支持这样的非final特性的局部变量在其中的方法中执行的话, 你能保证str的值的同步性吗?
并发的原因, 似乎并不可能, 说不定子线程输出的是主线程改变str后的值. 而在业务逻辑上, 我需要的是"china"这个值. 所以Java组织了这种用法, 用于规避线程之间的数据不同步问题;
问题1: 显式修饰为final的局部变量为何可以使用?
public static void test01() {
final int a = 0;
new Thread(()->{
System.out.println(a);
}).start();
}
因为final修饰的变量是无法改变了, 不会存在并发导致的数据不同步问题;
问题2: 为什么非显式修饰的局部变量, 只有不改变值, 也可以在lambda表达式中使用?
public static void test01() {
int a = 0;
new Thread(()->{
final int a = 0; // 假设编译通过
System.out.println(a);
}).start();
}
你可以这样去想象;
你也许还会有一个问题: 我在Lambda之外修改了, 还是会引发编译错误啊! 答案之前告诉过你, 因为并发, 你能确定在lambda里赋值的final变量和主线程的是同步的吗? 你不能的!