黑马程序员技术交流社区

标题: 为什么在局部上定义内部类只能引用final的局部变量? [打印本页]

作者: Neverbelazy    时间: 2013-4-20 20:15
标题: 为什么在局部上定义内部类只能引用final的局部变量?
本帖最后由 Neverbelazy 于 2013-4-25 10:42 编辑

如题,毕老师基础视频第10天中讲过内部类的问题,其中提到了 ——局部上定义内部类如果要使用局部变量,只能使用被final修饰的局部变量。
这是为什么?
例如:
  1. class Outer{
  2.          void method{
  3.                  final int x=0;
  4.          class Inner{
  5.                  void function(){
  6.                         System.out.print(x);
  7.                        }
  8.                 }
  9.         }
  10. }
复制代码
补充问题,
1.final修饰的局部变量的生命周期是多少?它是存放在常量池中的吗?(如果是,什么时候被产生,什么时候被释放的呢,做好能有一个代码或者引用文件的解释)
2.如果是生命周期的问题,为什么定义成static不能解决这个问题,而要定义成final?


个人推荐答案:#7 @陈迎春
首先,你要明确一点,在方法中定义的变量,常量,不管是被什么修饰的,即使是final修饰,当方法结束时这个变量,常量都会消失!

其次,要知道,被final修饰的常量,会在方法结束时,在局部内部类中备份一个一模一样的,里相当于,局部类中有了一个成员变量~这样即使常量消失了,内部类仍然可以使用该常量~要是还不怎么懂,你可以看下我以前发的贴~


---------------------分割线----------------------
前天在 云3版发了一贴,里面写了些我对这个问题的理解,同时也阐述了不确定的地方。
”讨论一个关于--定义在局部的内部类--的问题   http://bbs.itheima.com/thread-46656-1-1.html
云3版内得到了一些回复,但是还是没有解释我的疑问,在此重提,希望大家可以指点,谢谢!

作者: 吴家吉    时间: 2013-4-20 20:36
final修饰的局部变量就相当于是常量,存放在常量池中,如果不用final修饰,变量用过之后就会被释放,下次再用的时候就找不到这个变量了,而用final修饰后,还可以在常量池中找到.
作者: 井瑞涛    时间: 2013-4-20 20:37
对于局部变量,作用域为该方法内,方法执行结束该局部变量也随之消失。但内部类可能会产生隐式的“闭包”,闭包使局部变量脱离它所在的方法继续存在。若没用final修饰,也即变量的值可以随意改变,将会引起混乱。




作者: Neverbelazy    时间: 2013-4-21 00:14
井瑞涛 发表于 2013-4-20 20:37
对于局部变量,作用域为该方法内,方法执行结束该局部变量也随之消失。但内部类可能会产生隐式的“闭包”, ...

可否详细解释一下 “闭包”这个概念,它是怎么使局部变量脱离它所在的方法继续存在的?
作者: 赵海洋    时间: 2013-4-21 00:42
因为被final修饰的量可以认为是常量,这样引用时就不会出现引用后再次引用值改变,从而引起异常报错。
作者: Neverbelazy    时间: 2013-4-23 02:39
赵海洋 发表于 2013-4-21 00:42
因为被final修饰的量可以认为是常量,这样引用时就不会出现引用后再次引用值改变,从而引起异常报错。 ...

我的问题其实就是,为什么要定义成常量?问什么不可以在局部内被改变?
作者: 陈迎春    时间: 2013-4-23 10:42
首先,你要明确一点,在方法中定义的变量,常量,不管是被什么修饰的,即使是final修饰,当方法结束时这个变量,常量都会消失!

其次,要知道,被final修饰的常量,会在方法结束时,在局部内部类中备份一个一模一样的,里相当于,局部类中有了一个成员变量~这样即使常量消失了,内部类仍然可以使用该常量~要是还不怎么懂,你可以看下我以前发的贴~
作者: 赵海洋    时间: 2013-4-23 10:51
Neverbelazy 发表于 2013-4-23 02:39
我的问题其实就是,为什么要定义成常量?问什么不可以在局部内被改变? ...

你再问题中谈到了生命周期,应该就是生命周期的原因,因为方法内定义的变量是局部变量,离开该方法,变量就失去了作用,也就会自动被消除,而内部类却不会离开它所在方法就失去作用,它有更广的生命周期。也就是说内部类和外部类的生命周期应该是差不多的,而外部类中的非final变量的生命周期显然和类的生命周期不同,非final变量被调用后就会被释放,从而引起了内部类的非法调用。
java编译器的行为是这样的(前提条件是该变量在内部类中被引用):
    若定义为final,则java编译器则会在内部类生成一个外部变量的拷贝,而且可以既可以保证内部类可以引用外部属性,又能保证值的唯一性。
    若不定义为final,则无法通过编译。因为编译器不会给非final变量进行拷贝,那么内部类引用的变量就是非法的!
这是考虑了安全问题。
不可以在局部内改变,就是为了保证值的唯一性考虑的。
作者: 曹睿翔    时间: 2013-4-23 10:56
稍后云版块会推出学习过程中问题以及对知识点的领悟收集!会有技术分奖励,敬请期待、多多参与,要注意按照进度收集问题,那样你会事半功倍。
问题解决的话请及时结贴
作者: Neverbelazy    时间: 2013-4-23 13:16
本帖最后由 Neverbelazy 于 2013-4-23 13:23 编辑

刷下错误请看楼下
作者: Neverbelazy    时间: 2013-4-23 13:17
本帖最后由 Neverbelazy 于 2013-4-23 13:24 编辑

系统刷新错误 请看楼下
作者: xiewen    时间: 2013-4-23 18:41
方法内部类对象不能使用该内部类所在方法的非final局部变量。
因为方法的局部变量位于栈上,只存在于该方法的生命期内。当一个方法结束,其栈结构被删除,局部变量成为历史。但是该方法结束之后,在方法内创建的内部类对象可能仍然存在于堆中!例如,如果对它的引用被传递到其他某些代码,并存储在一个成员变量内。正因为不能保证局部变量的存活期和方法内部类对象的一样长,所以内部类对象不能使用它们。
下面是完整的例子:
    class Outer {
        public void doSomething(){
            final int a =10;
            class Inner{
                public void seeOuter(){
                    System.out.println(a);
                }
            }   
            Inner in = new Inner();
            in.seeOuter();
        }
        public static void main(String[] args) {
            Outer out = new Outer();
            out.doSomething();
        }
     }
作者: Neverbelazy    时间: 2013-4-23 21:21
xiewen 发表于 2013-4-23 18:41
方法内部类对象不能使用该内部类所在方法的非final局部变量。
因为方法的局部变量位于栈上,只存在于该方法 ...

我明白了你描述的意思:
可是问题是 final int a=10的生命周期到底是多少,它的周期比所在局部长吗?如果比局部长的话,为什么在局部外就不能调用a?

作者: breeze    时间: 2013-4-23 21:40
本帖最后由 breeze 于 2013-4-23 21:52 编辑
Neverbelazy 发表于 2013-4-23 21:21
我明白了你描述的意思:
可是问题是 final int a=10的生命周期到底是多少,它的周期比所在局部长吗?如果 ...

final修饰的变量是常量, 不是存在堆栈中, 就是将一个固定的值给了内部类
public class Test {

        public static void main(String[] args) {
                method();
        }
        /*
        1. int a=10; 如此定义局部变量时,编译无法通过,提示必须使用final修饰
        2. 作用域问題,局部变量作用方法只在方法里,也就是方法的"{}"之间有用
        3. 定义成final可以,是因为a就变成了常量,常量当然哪里都能用,例如:int a =10,10就是常量
        */        
        public static void method(){
                          final int a=10;
                          class Inner{
                                  void fun(){
                                          System.out.println(a);
                                  }
                          }
                          new Inner().fun();
        }
}

作者: Neverbelazy    时间: 2013-4-23 21:54
陈迎春 发表于 2013-4-23 10:42
首先,你要明确一点,在方法中定义的变量,常量,不管是被什么修饰的,即使是final修饰,当方法结束时这个 ...

ok 明白了! 内部类中有备份,多谢!
作者: Neverbelazy    时间: 2013-4-23 21:55
xiewen 发表于 2013-4-23 18:41
方法内部类对象不能使用该内部类所在方法的非final局部变量。
因为方法的局部变量位于栈上,只存在于该方法 ...

多谢,问题已经解决!
作者: Neverbelazy    时间: 2013-4-23 21:56
breeze 发表于 2013-4-23 21:40
final修饰的变量是常量, 不是存在堆栈中, 就是将一个固定的值给了内部类
public class Test {

多谢,7#的回复最直接的解决了我的问题,可以参考。




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