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

先来验证局部内部类中只能访问所在方法的最终变量或参数以及实际上的最终变量或参数。如程序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句代码就有了编译错误。可以得到,在局部内部类中只能访问所在方法的最终变量或参数以及实际上的最终变量或参数。
class Out_1 {

   public void method(final int a) {
       final int b = 1;
       int c = 2;
       c = 3;
       class Inner_1 {
           int d = a;    //编译正确。
           int e = b;    //编译正确。
           int f = c;    //编译错误。
       }
    }

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

   public void method1() {
       int m = 4;
       class Inner_2 {
           public void method2() {
               System.out.println(m);
           }
       }
       new Thread("t") {
           public void run() {
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               new Inner_2().method2();
           }
       }.start();
   }

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

   public void method1() {
       int m;
       class Inner_3 {
           public void method2() {
               // 编译错误,m应在定义内部类之前初始化。
               m = 4;
           }
       }
   }

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

上一篇:如何让 Git 使用 HTTP 代理服务器


下一篇:PHP内核探索之变量(6)- 后续内核探索系列大纲备忘