Java内部类访问变量需final修饰的相关规则疑问澄清
先纠正一个小认知偏差:你提到的“全局变量”实际是类的实例成员变量,Java本身不存在全局变量的概念。结合你给出的代码,两个疑问的解答如下:
疑问1解答
你的推论完全正确。要求被内部类引用的局部变量为final(Java 8之后只要是实际未修改的effectively final也符合要求),核心目的就是避免开发者产生认知偏差:
底层实现中,匿名内部类实例化时确实会复制一份局部变量的副本存到自己的实例字段里,你在内部类中操作的永远是这个副本,和onCreate方法栈中的原局部变量i完全独立。如果Java不强制加final限制,你写i++的时候会误以为修改的是方法里的原变量,实际两边值完全不同步,只会产生难以排查的逻辑bug。
你提到的“只是给开发者的提示而非底层强制必需”也符合实际:其他JVM语言比如Kotlin就没有这个限制,它会自动把被内部类引用的局部变量包装成单元素数组,让内部类和外部方法操作同一个数组对象来同步值,只是Java选择了用final限制的方案来降低语言复杂度,避免隐性性能开销。
你给出的代码片段编译时会在i++的位置直接报错,就是因为i不是final也不是effectively final。
疑问2解答
你的推论基本正确。成员变量j不需要final修饰,本质就是因为它不需要被复制:
你用到的OnClickListener属于非静态匿名内部类,实例化时会自动持有外部Activity类的实例引用。你在内部类里写j++,编译后实际执行的是OuterActivity.this.j++,直接通过外部类引用访问原成员变量,不存在副本机制,自然没有内外值不同步的问题,也就不需要final限制,j++的操作会直接修改Activity实例的成员变量值。
内容的提问来源于stack exchange,提问作者Steven

