温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Java Final与Effectively Final

发布时间:2020-08-07 10:49:21 来源:ITPUB博客 阅读:157 作者:慕容扶苏 栏目:编程语言

1. 介绍

Java 8 引入的众多功能中,其中一个最有趣的功能是 effectively final。即不用 final 修饰符也能达到同样的效果。

本文将介绍该功能的起源以及编译器处理 effectively final 与 final 关键字的不同之处。此外,还会通过一个 effectively final 变量的问题案例给出解决方案。

2. Effectively Final 的起源

简而言之,如果对象或基础类型的变量在初始化后值不发生改变,则可以把它们看做 effectively final。只要不改变对象引用,即使引用的对象发生状态改变,该对象也是 effectively final。

在 Java 引入该功能之前,不能在匿名类中使用非 final 局部变量。此外,也不能在匿名类、内部类和 lambda 表达式中多次赋值。新功能的加入节省了为 effectively final 变量输入 final 关键字的工作。

匿名类是一种内部类,不能访问非 final 变量或 effectively final 变量,也无法按照 JLS 8.1.3 的规定在其封闭作用域内的变量进行修改。lambda 表达式也有类似的限制,修改变量可能会带来并发问题。

docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.1.3

3. Final vs Effectively Final

要确认一个 final 变量是不是 effectively final,最简单的办法就是删除 final 关键字看能否编译并运行:

Java Final与Effectively Final

重新赋值或者改变 effectively final 都会报告无效代码。

3.1 编译器处理

JLS 4.12.4 指出,从方法参数或局部变量中删除 final 修饰符且不产生编译错误,则该变量为 effectively final。在程序中为变量声明加上 final 关键字,那么该变量也会变成 effectively final。

docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.12

与 final 变量不同,Java 编译器不会对 effectively final 变量进行额外优化。

下面这个简单的示例中声明两个 final String 变量,仅用作字符串连接:

Java Final与Effectively Final

编译器会将上面 main 方法实际执行的代码变成下面这样:

Java Final与Effectively Final

去掉 final 关键字,这些变量将被视为 effectively final。但编译器不会因为它们仅用作字符串连接而对它们优化。

4. 原子操作

在 lambda 表达式和匿名类中修改变量不是一个好习惯。因为我们不知道这些变量在方法块中会如何使用,在多线程环境中修改也可能会得到意外的结果。

关于使用 lambda 表达式的最佳实践已经有了一个教程,另外还有一个教程关于修改lambda 表达式中常见的反模式。有一种替代方案可以在这种场景中修改变量,通过原子性实现线程安全。

java.util.concurrent.atomic 提供了像 AtomicReference 和 AtomicInteger 这样的类。可以使用原子操作修改 lambda 表达式中的变量:

Java Final与Effectively Final

5. 总结

本文介绍了 final 变量和 effectively final 变量的区别,并且介绍了一种安全修改 lambda 函数变量的方案。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI