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

2018-11-09 02:36:12来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

  欢迎加入Java交流群 512878347

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

  先来验证局部内部类中只能访问所在方法的最终变量或参数以及实际上的最终变量或参数。如例1_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;    //编译正确,访问final修饰的参数。
8                int e = b;    //编译正确,访问实际上的最终变量。
9                int f = c;    //编译错误,c既不是最终变量
10                             //也不是实际上的最终变量。
11            }
12        }
13    } 

1_1 Out_1.java

  接着再来讨论为什么局部内部类中只能访问所在方法的最终变量或实际上的最终变量。这里只讨论方法的变量,对于方法的参数它的道理是一模一样的,因为方法的参数本质上就是方法的变量。要知道局部内部类和外部类是处于同一级别的,局部内部类不会因为定义在方法中就随着方法的执行完毕而销毁。如例1_2所示,在外部类Out_2的method1方法中定义了一个局部内部类Inner_2,然后创建了一个线程t并启动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    }

1_2 Out_2.java

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

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

1_3 Out_3.java

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

 


标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:Spring

下一篇:Spring 定时任务之 @Scheduled cron表达式