Java Lambda 表达式中为何不能访问局部定义的变量?

问题展示代码:

    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变量和主线程的是同步的吗? 你不能的!

posted @ 2024-06-22 15:19  老婆大人很温柔  阅读(5)  评论(0编辑  收藏  举报