为什么java内部类访问局部变量必须声明为final?

https://blog.csdn.net/z55887/article/details/49229491

先抛出让我疑惑了很久的一个问题

编程时,在线程中使用局部变量时候经常编译器会提示:局部变量必须声明为final

package test;

public class ThreadTest {

public void function(String a) {

new Thread(){
@Override
public void run() {
System.out.println(a);
}
}.start();
}

public static void main(String[] args) {
new ThreadTest().function("a");

}
}

上图中由于方法function中的形参a没有声明为final,编译抛出异常:Cannot refer to the non-final local variable a defined in an enclosing scope

这个问题我特意问过老师,也百度过,都没有给出满意的解答。今天看安卓视频无意发现了答案,真是意外之喜啊!

其实原因就是一个规则:java内部类访问局部变量时局部变量必须声明为final。

那为什么要这样呢?还有线程为什么和内部类一样?接下来我们慢慢揭秘。

public class Out {

public void test(final String a) {
class In{

public void function() {
System.out.println(a);
}
}
new In().function();
}

public static void main(String[] args) {
new Out().test("hi");
}
}
编译这个类后发现产生了两个class文件

也就是说内部类和外部类各一个class文件,这样就产生了一个问题,调用内部类方法的时候如何访问外部类方法中的局部变量呢?

实际上编译后的内部类的构造方法的里面,传了对应的外部类的引用和所有局部变量的形参。

(由于外部类方法执行完后局部变量会消亡,所以内部类构造函数中的局部变量实际是一份“复制”。而为了访问外部类中的私有成员变量,外部类编译后也产生了访问类似与getXXX的方法。)

这时产生了一个不一致的问题,如果局部变量不设为final,那内部类构造完毕后,外部类的局部变量又改变了那怎么办?

public class Out {

public void test(String a) {
class In{
public void function() {
System.out.println(a);
}
}
a="hello";
new In().function();
}

public static void main(String[] args) {
new Out().test("hi");
}
}

如代码中所示,这样调用内部类方法时会造成外部类局部变量和内部类中对应的变量的不一致。(注意内部类编译成class文件与new无关,a="hello"放在new In()前后不影响不一致关系,new在jvm运行class文件时才起效)

理解完内部类必须访问final声明的局部变量原因,我们回到最开始的问题:为什么线程和内部类一样

因为线程也是一个类,所以new Thread也相当于创建了一个内部类啦

我们编译一下最开始的ThreadTest.java文件

发现线程编译后也是产生了单独的class文件。

至此,问题全部解决啦~~

最后说明一下java1.8和之前版本对这个规则编译的区别。

如果在1.8的环境下,会很神奇的发现我们最开始的ThreadTest.java文件编译和运行是完全没有问题的,也就是说内部类使用的局部变量是可以不声明为final?!

且慢,如果我们给局部变量再赋下值会发现编译又会出现同样的错误

public class ThreadTest {

public void function(String a) {
a="b";
new Thread(){
@Override
public void run() {
System.out.println(a);
}
}.start();
}

public static void main(String[] args) {
new ThreadTest().function("a");

}
}
在a="b"这一行报错:Local variable a defined in an enclosing scope must be final or effectively final
也就是说规则没有改变,只是java1.8的编译变得更加智能了而已,在局部变量没有重新赋值的情况下,它默认局部变量为final型,认为你只是忘记加final声明了而已。如果你重新给局部变量改变了值或引用,那就无法默认为final了,所以报错。

参考网站:

详细原理版:java内部类访问局部变量时的final问题 https://blog.csdn.net/xiancaieeee/article/details/8834352

对照原理版:关于java里方法的内部类只能访问被final修饰的局部变量和... http://bbs.itheima.com/thread-136974-1-1.html

jdk不同带来的区别:Java中方法内定义的内部类可以访问方法中的局部变量的问题 https://bbs.csdn.net/topics/390918289?page=1

原文地址:https://www.cnblogs.com/jianghengsh/p/11129453.html

时间: 2024-11-13 00:49:00

为什么java内部类访问局部变量必须声明为final?的相关文章

内部类访问局部变量的时候,为什么变量必须加上final修饰

这里的局部变量就是在类方法中的变量,能访问方法中变量的类当然也是局部内部类了.我们都知道,局部变量在所处的函数执行完之后就释放了,但是内部类对象如果还有引用指向的话它是还存在的.例如下面的代码: class Outer{ public static void main(String[] args){ Outer out = new Outer(); Object obj = out.method(); } Object method(){ int locvar = 1; class Inner{

final运用于内部类访问局部变量

public void mRun( final String name){ new Runnable() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(name); } }.run(); } 当内部类访问局

局部内部类访问局部变量的问题

局部内部类访问局部变量的注意事项: 局部变量必须用final修饰! 为什么?   因为局部变量是随着方法的调用而调用,随着调用完毕而消失 但是我们调用内部类时创建的对象依旧在堆内存中,并没有被回收,如果访问的局部变量不是用final修饰的,就是当方法调用完毕后,依旧存在于堆内存中的对象找不到局部变量的问题 而此时被final修饰的变量可以看成是一个常量,存在于常量池中,不会被立刻回收. 原文地址:https://www.cnblogs.com/afei1013/p/12349608.html

方法中的内部类,操作本方法中的局部变量时,局部变量应该声明为final!!

如题! public void doit(){   final int i=9;        Thread t=new Thread(new Runnable(){ @Override            public void run() {                // TODO Auto-generated method stub                i++;            }                    });

JAVA中内部类(匿名内部类)访问的局部变量为什么要用final修饰?

本文主要记录:在JAVA中,(局部)内部类访问某个局部变量,为什么这个局部变量一定需要用final 关键字修饰? 首先,什么是局部变量?这里的局部是:在方法里面定义的变量. 因此,内部类能够访问某局部变量,说明这个内部类不是在类中定义的内部类,而是在方法中定义的内部类,称之为:局部内部类. 局部变量的作用域:局部变量是在某个方法中定义,当该方法执行完成后,局部变量也就消失了.[局部变量分配在JVM的虚拟机栈中,这部分内存空间随着程序的执行自动回收],也即:局部变量的作用域是在 “方法的范围内”.

什么是Java内部类?

内部类 (一) 概述 把类定义在另一个类的内部,该类就被称为内部类. 举例:把类Inner定义在类Outer中,类Inner就被称为内部类. class Outer {    class Inner {   } } (二) 内部类的访问规则 ? A:可以直接访问外部类的成员,包括私有 ? B:外部类要想访问内部类成员,必须创建对象 (三) 内部类的分类 ? A:成员内部类 ? B:局部内部类 ? C:静态内部类 ? D:匿名内部类 (1) 成员内部类 成员内部类——就是位于外部类成员位置的类 特

java 内部类2(成员内部类)

成员内部类: 特点:在其所在的外部类,的成员函数中,的类. 难点:看注释(涉及到jvm) /*test()执行完毕时,x2从内存中消失,inner的声明周,比x2长,inner还在访问,给人的感觉好像血 的生命周期变长了 解决办法:让内部类访问局部变量的复制品,就是此x2 非彼x2,两份数据一份消失. */ public class EX10 { public static void main(String[] args) { Outer out = new Outer(); out.text(

【Java基础】Java内部类

什么是内部类 把类定义在其他类的内部,这个类就被称为内部类. 内部类的分类 内部类分为两种,分别为成员内部类和局部内部类: 成员内部类:和成员变量和成员方法定义在同级 局部内部类:和局部变量定义在同级,包括在构造方法,成员方法体和静态方法体中 class Outer { class InnerA {//成员内部类 } { class InnerB {//局部内部类 } } static { class InnerC {//局部内部类 } } public Outer() { class Inne

内部类访问的局部变量必须加final

(1)内部类是外部类的一个成员,就像外部类的成员方法一样,所以内部类有权限访问外部类的所有成员,包括private的. (2)内部类不能访问外部类方法中的局部变量,除非变量是final的(一般发生在方法中定义的内部类).这是因为局部变量的生命周期原因. class Outer{ private int a; public class Inner{ private int a; public void method(int a){ a++;    //局部变量 this.a++;    //Inn