A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 钟伟杰 于 2013-4-19 09:17 编辑
class Outer
{
        int x = 5;               
        void function()
        {
                final int y = 8;                //function()内的局部变量,只有被final修饰后才能被同为function()内的类访问。
                class Inner               
                {        
                        void method()
                        {
                                System.out.println(y);
                        }               
                }
                new Inner().method();
        }
}

class Test2
{
        public static void main(String[] args)
        {
                new Outer().function();
        }
}
在上面的内部类作为function()的局部元素访问所在方法的局部变量时要加 final这点不理解,它到底是怎么运作的。

评分

参与人数 1技术分 +1 收起 理由
黄玉昆 + 1

查看全部评分

5 个回复

倒序浏览
加final的原因:
    加上final的变量,变成了常量,常量存储在方法区的常量池,这里边的内容生命最长,跟随类的加载而加载,跟随类的消失,而消失;
   不加final,变量就是局部变量,跟随方法的存在而存在,跟随方法的消失而消失,生命周期相对很短,内部类需要对象调用,对象时存在于堆内存,随着虚拟机收
   取垃 圾时才消失,对象的生命周期,大于了局部变量的生命周期,所以内部类定义在局部位置上,访问局部的变量,这个局部变量的生命必须要大于对象的生命,
   因此加final,将这个变量变成常量。
你可以看下面一个例子:
  1. interface A
  2. {
  3. void method();
  4. }
  5. class Outer
  6. {
  7. public void show(){

  8. final int x = 9;//成员方法中的局部变量

  9. class Inner implements A {//内部类实现接口A,内部类的生命周期
  10. //会长于不加final修饰的局部变量,此时若调用局部变量会报错

  11. public void method(){

  12. System.out.println("x="+x);
  13. }
  14. }
  15. Inner in = new Inner();//在成员方法中新建
  16. in.method();//内部类的对象,访问内部类方法
  17. }
  18. }
  19. class InnerClassDemo3
  20. {
  21. public static void main(String[] args)
  22. {
  23. Outer o = new Outer();
  24. o.show();
  25. //A a = o.func();
  26. }
  27. }
复制代码
若不加final,变量x将随着方法show()的消失而消失,就不能实现接口A,因为内部类要想调用接口A,生命周期就必须大于局部变量X的生命周期导致无法访问。

评分

参与人数 1技术分 +1 收起 理由
黄玉昆 + 1

查看全部评分

回复 使用道具 举报
楼上解释的十分透彻。
内部类定义在类中的局部位置上时只能访问该局部被final修饰的局部变量
所以要想内部类中能访问局部变量y,必须要定义y为final常量型。为了就是是局部变量的生命周期变长,
方便内部类对象的调用。

评分

参与人数 1技术分 +1 收起 理由
黄玉昆 + 1

查看全部评分

回复 使用道具 举报
2楼解释的好详细.没什么好补充的了
回复 使用道具 举报
刚上网查了下,站在程序员的角度思考这个问题
为什么匿名内部类参数必须为final类型
1)  从程序设计语言的理论上:局部内部类(即:定义在方法中的内部类),由于本身就是在方法内部(可出现在形式参数定义处或者方法体处),因而访问方法中的局部变量(形式参数或局部变量)是天经地义的.是很自然的
2)  为什么JAVA中要加上一条限制:只能访问final型的局部变量?
3)  JAVA语言的编译程序的设计者当然全实现:局部内部类能访问方法中的所有的局部变量(因为:从理论上这是很自然的要求),但是:编译技术是无法实现的或代价极高.
4)  困难在何处?到底难在哪儿?
     局部变量的生命周期与局部内部类的对象的生命周期的不一致性!

5)  设方法f被调用,从而在它的调用栈中生成了变量i,此时产生了一个局部内部类对象inner_object,它访问了该局部变量i .当方法f()运行结束后,局部变量i就已死亡了,不存在了.但:局部内部类对象inner_object还可能   一直存在(只能没有人再引用该对象时,它才会死亡),它不会随着方法f()运行结束死亡.这时:出现了一个"荒唐"结果:局部内部类对象inner_object要访问一个已不存在的局部变量i!

6)  如何才能实现?当变量是final时,通过将final局部变量"复制"一份,复制品直接作为局部内部中的数据成员.这样:当局部内部类访问局部变量时,其实真正访问的是这个局部变量的"复制品"(即:这个复制品就代表了那个局部变量).因此:当运行栈中的真正的局部变量死亡时,局部内部类对象仍可以访问局部变量(其实访问的是"复制品"),给人的感觉:好像是局部变量的"生命期"延长了.

那么:核心的问题是:怎么才能使得:访问"复制品"与访问真正的原始的局部变量,其语义效果是一样的呢?
当变量是final时,若是基本数据类型,由于其值不变,因而:其复制品与原始的量是一样.语义效果相同.(若:不是final,就无法保证:复制品与原始变量保持一致了,因为:在方法中改的是原始变量,而局部内部类中改的是复制品)

当变量是final时,若是引用类型,由于其引用值不变(即:永远指向同一个对象),因而:其复制品与原始的引用变量一样,永远指向同一个对象(由于是final,从而保证:只能指向这个对象,再不能指向其它对象),达到:局部内部类中访问的复制品与方法代码中访问的原始对象,永远都是同一个即:语义效果是一样的.否则:当方法中改原始变量,而局部内部类中改复制品时,就无法保证:复制品与原始变量保持一致了(因此:它们原本就应该是同一个变量.)

一句话:这个规定是一种无可奈何.也说明:程序设计语言的设计是受到实现技术的限制的.这就是一例. 因为:我就看到不少人都持这种观点:设计与想法是最重要的,实现的技术是无关紧要的,只要你作出设计与规定,都能实现.
引用来自:http://blog.csdn.net/salahg/article/details/7529091
回复 使用道具 举报
Java要求所有被内部类访问的局部变量都使用final修饰的原因:

对于普通局部变量而言,它的作用域就是停留在该方法内,当方法执行结束,该局部变量也随之消失;
但内部类则可能产生隐式的“闭包(Closure)”,使得局部变量脱离它所在的方法继续存在。

举个局部变量脱离其所在方法继续存在的例子:
public class ClosureTest
{
    public static void main(String[] args)
    {
         //定义一个局部变量
        final String str = "Java";
        //在内部类里访问局部变量str
        new Thread(new Runnable()
       {
           public void run()
           {
                for (int i = 0; i < 100 ; i++ )
                {
                     //此处将一直可以访问到str局部变量
                     System.out.println(str + " " + i);
                     //暂停0.1秒
                     try
                     {
                          Thread.sleep(100);
                     }
                     catch (Exception ex)
                     {
                          ex.printStackTrace();
                     }
                }
           }
       }).start();                     //①
      //执行到此处,main方法结束
     }
}

第一条粗体代码定义了一个局部变量str。正常情况下,当程序执行完①行代码之后,main方法的生命周期就结束了,
局部变量str的作用域也会随之结束。但实际上这个程序中只要新线程里的run方法没有执行完,匿名内部类的实例的
生命周期就没有结束,将一直可以访问str局部变量的值,这就是内部类会扩大局部变量作用域的实例。

由于内部类可能扩大局部变量的作用域,如果再加上这个被内部类访问的局部变量没有使用final修饰,也就是说该变量
的值可以随便改变,那将引起极大的混乱,因此Java编译器要求所有被内部类访问的局部变量必须使用final修饰符修饰。

  纯手打,觉得这个例子讲得很透彻,摘自《疯狂Java:突破程序员基本功的16课(修订版)》2.4 final修饰符 p.69~70

评分

参与人数 1技术分 +1 收起 理由
黄玉昆 + 1

查看全部评分

回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马