黑马程序员技术交流社区

标题: 关于内部类访问局部变量的时候,为什么变量必须加上final... [打印本页]

作者: 夕阳的刻痕    时间: 2015-6-17 00:34
标题: 关于内部类访问局部变量的时候,为什么变量必须加上final...
本帖最后由 夕阳的刻痕 于 2015-6-17 00:37 编辑

今天面试老师问这题把我给问蒙了  T-T
-------------------------------------------------------------------------------------------------------------
这里的局部变量就是在类方法中的变量,能访问方法中变量的类当然也是局部内部类了。
我们都知道,局部变量在所处的函数执行完之后就释放了,但是内部类对象如果还有引用指向的话它是还存在的。例如下面的代码:
classOuter{                                                                       
        publicstaticvoidmain(String[] args){
                Outer out = new Outer();
                Object obj = out.method();
        }   

        Object method(){
                int locvar = 1;
                classInner{
                        voiddisplayLocvar(){
                                System.out.println("locvar = " + locvar);
                        }
                }
                Object in = new Inner();
                returnin;
        }
}

当out.method()方法执行结束后,局部变量 locvar 就消失了,但是在method()方法中 obj in = new Inner() 产生的 in 对象还存在引用obj,这样对象就访问了一个不存在的变量,是不允许的。这种矛盾是由局部内部类可以访问局部变量但是局部内部类对象和局部变量的生命周期不同而引起的。
局部内部类访问局部变量的机制

在java中,类是封装的,内部类也不例外。我们知道,非静态内部类能够访问外部类成员是因为它持有外部类对象的引用 Outer.this, 就像子类对像能够访问父类成员是持有父类对象引用super一样。局部内部类也和一般内部类一样,只持有了Outer.this,能够访问外部类成员,但是它又是如何访问到局部变量的呢?
实际上java是将局部变量作为参数传给了局部内部类的构造函数,而将其作为内部类的成员属性封装在了类中。我们看到的内部类访问局部变量实际上只是访问了自己的成员属性而已,这和类的封装性是一致的。那么上面的代码实际上是这样:

Object method(){
                int locvar = 1;
                classInner{
                    privateint obj;
                    publicInner(int obj){
                        this.obj = obj;
                    }
                        voiddisplayLocvar(){
                                System.out.println("locvar = " + locvar);
                        }
                }
                Object in = new Inner(locvar);  //将locvar作为参数传给构造,以初始话成员returnin;
        }
那么问题又来了, 那么为什么外部变量要是final的呢?即使外部变量不是final,编译器也可以如此处理:自己定义一个同类型的变量,然后在构造方法中赋值就行了。原因就是为了让我们能够挺合逻辑的直接使用外部变量,而且看起来是在始终使用 外部的变量(而不是赋值以后的自己的字段)。
将函数的参数引用设为final,主要是考虑到局部变量的生命周期与局部内部类的对象的生命周期的不一致性!往深层次说,就是为了解决参数的不一致性问题。即 因为从编程人员的角度来看他们是同一个东西, 如果编程人员在程序设计的时候在内部类中改掉参数的值,但是外部调用的时候又发现值其实没有被改掉,这就让人非常的难以理解 和接受,为了避免这种尴尬的问 题存在,所以编译器设计人员把内部类能够使用的参数设定为必须是final来规避这种莫名其妙错误的存在。
       举个例子来说可能会更清楚一些,对于局部变量int i=3;方法中代码修改的是这个真正的变量i,而内部为对象修改的是i的复制品copy_i,这样这两个i就会发生值的不一致性,(这一点正是这个实现技术的缺陷),所以干脆就不允许这个int i=3;局部变量发生值的改变!由于不允许改int i的值,所以这两个int i的值就始终保持值的一致了,这才是final的这个规定的由来! 是一种不得不如此的无奈之举!
      简单的来说,因为生命周期的原因,内部类需要复制局部变量为内部类的一个成员变量,因为复制,为了保证局部变量和该副本的值的一致性,所以要将修饰符设为final。
作者: world.net    时间: 2015-6-17 01:22
生命周期不同啊
作者: 夕阳的刻痕    时间: 2015-6-17 09:07
world.net 发表于 2015-6-17 01:22
生命周期不同啊

然后老师问我为什么加Final修饰就可以了,我当时这问题就没回答上来,还是对那些东西没理解透啊
作者: 夕阳的刻痕    时间: 2015-6-17 09:22
http://blog.csdn.net/zhangjg_blog/article/details/19996629

作者: world.net    时间: 2015-6-17 15:36
夕阳的刻痕 发表于 2015-6-17 09:07
然后老师问我为什么加Final修饰就可以了,我当时这问题就没回答上来,还是对那些东西没理解透啊 ...

说的我都紧张了
作者: lucien_he    时间: 2015-6-17 16:26
- -   感觉  面试好吓人  




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2