局部内部类访问所在方法的变量或参数


  欢迎加入Java交流群 512878347 ,欢迎关注微信公众号 以文在线 。

  局部内部类是在方法中定义的类。它的可见范围是当前方法,和局部变量一样,局部内部类不能用访问控制修饰符(public、private以及protected)和静态修饰符static来修饰。局部内部类中除了可以访问外部类的所有成员,还可以访问所在方法的最终变量或参数(被final修饰的变量或参数),从JDK8.0开始,还可以访问所在方法的实际上的最终变量或参数(没有被final修饰但只进行了一次赋值的变量或参数)。为什么局部内部类中只能访问所在方法的最终变量或参数以及实际上的最终变量或参数呢?

  先来验证局部内部类中只能访问所在方法的最终变量或参数以及实际上的最终变量或参数。如程序1所示,在外部类Out_1的method方法中有一个参数a,两个变量b、c,并在局部内部类Inner_1中访问了这三个量。由于参数a是被final修饰的,那参数a就是最终的参数,故在Inner_1中可以访问它,第7句代码没有编译错误;虽然变量b没有被final修饰,但是变量b仅仅被赋值一次,它是实际上的最终变量,故在Inner_1中可以访问它,第8句代码没有编译错误;变量c没有被final修饰并且被赋值了两次,它不是最终变量也不是实际上的最终变量,故在Inner_1中不可以访问它,第9句代码就有了编译错误。可以得到,在局部内部类中只能访问所在方法的最终变量或参数以及实际上的最终变量或参数。

 1    class Out_1 { 2        public void method(final int a) { 3            final int b = 1; 4            int c = 2; 5            c = 3; 6            class Inner_1 { 7                int d = a;    //编译正确。
 8                int e = b;    //编译正确。
 9                int f = c;    //编译错误。
10 } 11 } 12    }

程序1 Out_1.java 

  接着再来讨论为什么局部内部类中只能访问所在方法的最终变量或实际上的最终变量。这里只讨论方法的变量,对于方法的参数它的道理是一模一样的,因为方法的参数本质上就是方法的变量。要知道局部内部类和外部类是处于同一级别的,局部内部类不会因为定义在方法中就随着方法的执行完毕而销毁。如程序2所示,在外部类Out_2的method1方法中定义了一个局部内部类Inner_2,然后创建了一个线程t并启动它,然后method1方法就执行结束,局部变量m就被销毁。线程t启动之后,会先睡眠1000毫秒,然后创建了局部内部类Inner_2的对象,并通过该对象去调用了method2方法,在method2方法中访问了method1方法定义的变量m,由于method1方法早已执行结束,变量m已经消失。这样就出现了一个矛盾:内部类对象访问了一个不存在的变量。

 1    class Out_2 { 2        public void method1() { 3            int m = 4; 4            class Inner_2 { 5                public void method2() { 6 System.out.println(m); 7 } 8 } 9            new Thread("t") { 10                public void run() { 11                    try { 12                        Thread.sleep(1000); 13                    } catch (InterruptedException e) { 14 e.printStackTrace(); 15 } 16                    new Inner_2().method2(); 17 } 18 }.start(); 19 } 20    }

程序2    Out_2.java 

  为了解决这个矛盾,如果局部内部类中访问了所在方法的某个变量,就将该方法中的变量复制一份作为内部类的成员变量,当内部类访问所在方法中的变量时,就让它去访问复制出来的成员变量。这样在内部类所在方法中的变量消失的之后,仍然可以访问它,当然这里并不是真正地访问它,而是访问它的复制品。这里需要注意,由于是将局部内部类所在方法的变量复制一份作为局部内部类的成员变量,故在定义局部内部类之前,一定要对局部内部类所在方法的变量进行初始化,没有初始化是无法复制的。在程序3所示的代码中,第7句代码是有编译错误的。

 1    class Out_3 { 2        public void method1() { 3            int m; 4            class Inner_3 { 5                public void method2() { 6                    // 编译错误,m应在定义内部类之前初始化。
 7                    m = 4; 8 } 9 } 10 } 11    }

程序3    Out_3.java 

  但是这时又有一个问题,那就是必须时时刻刻保证复制得到的那一份成员变量的值和原来的局部变量的值相同。如果在外部类中修改了局部变量的值,那就要修改局部内部类中复制得到的那一份成员变量的值;如果在局部内部类中修改了复制得到的那一份成员变量的值,那就要修改外部类中局部变量的值(前提是这个局部变量还存在),这样做是非常困难的。于是Java干脆就不允许局部内部类要访问的局部变量的值发生改变,即局部内部类中只能访问所在方法的最终变量或实际上的最终变量。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM