ycyz311
2015-10-16 17:51
采纳率: 0%
浏览 4.3k

Java中为什么匿名对象中方法,调用局部变量必须加final呢,求详解

Java中为什么匿名对象中方法,调用局部变量必须加final呢,求详解。在网上找了很久,基本没看到想要的答案,最好从Java虚拟机内存分配角度讲一讲,其他角度亦可。

老师只告诉这么用就好了,问了也说不知道,匿名对象在哪没研究过

求大神回复

  • 写回答
  • 好问题 提建议
  • 关注问题
  • 收藏
  • 邀请回答

5条回答 默认 最新

  • weixin_29987797 2015-10-16 22:57

    在main方法中有一个变量a,之后再在main方法中开启一个线程,并且在此线程中使用了a,如果a不是final的话,那么当main函数执行完,a就不存在了。而你在线程中还要使用这个变量,显然要定义成final。定义成final的目的是增强变量的生存期,使在匿名内部类对象中使用的时候这个变量还存在。

    评论
    解决 无用
    打赏 举报
  • 毕小宝 2015-10-17 01:29

    局部内部类(包括匿名局部内部类和普通内部类)中使用局部变量,那么这个局部变量必须使用final修饰。这里说的事局部内部类,不是普通静态内部类和非静态内部类,因为他们不能访问方法体内的局部变量。

    java要求所有被局部内部类访问的局部变量都是用final修饰是因为:对于普通局部变量他的作用域就是该方法内,当方法结束该局部变量就随之消失;但局部内部类可能产生隐式的“闭包”,闭包将使得局部变量脱离他所在的方法继续存在。
    请看下面的例子是局部变量脱离他所在的方法继续存在的例子:

     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秒 www.sctarena.com 
         try  
         {  
          Thread.sleep(100);  
         }  
         catch (Exception ex)  
         {  
          ex.printStackTrace();  
         }  
        }  
       }  
      }).start();               //①  
      //执行到此处,main方法结束  
     }  
    }  
    

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

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

    评论
    解决 无用
    打赏 举报
  • IT_Trojan 2015-10-17 04:00

    final增强内部变量的生存期

    评论
    解决 无用
    打赏 举报
  • 诸相非相 2015-10-17 16:04
    先插一句,java8开始匿名内部类使用的外部变量不再被强制用final修饰。外部变量要么是final的,要么自初始化后值不会被改变,这两种都是可以在匿名内部类中使用且编译通过。否则就编译报错:error: local variables referenced from an inner class must be final or effectively final。
    我觉得类似“final增强内部变量的生存期”这样的说法不完全准确。
    先说一下final的外部变量。匿名内部类编译后,final的外部变量(就是常量了),存储到常量池中的,编译期间,匿名内部类使用到该常量的地方都会被替换为对应的“常量值”,比如使用了外部的String str = "java" , 匿名内部类所有用到str的地方都会被替换为"java" 。这样一来,从字节码级别看,内部类引用外部变量的痕迹被抹得一干二净。
    再说一下非final的外部变量。当一个匿名内部类被创建时,其构造函数是自动生成的,如果有在内部类中使用到外部变量(非final修饰),那么这种外部变量是作为匿名内部类构造函数的参数,传递到内部类中使用的。当然,这种参数传递是一种传值传递(实际上所有java的参数传递都传值的,基本类型自不必说,Object及其子类作为函数参数,实际上传的值,就是对象在内存的存储地址),因此在内部类中对该外部变量的“值”修改是无效的;另外,如果外部变量在外部类中有修改,那么内部类使用该变量时可能值已经不是预期的那个“数”了。所以内部类使用的外部变量,不允许有任何的修改才能避免上述问题。
    所以java编译器会检查匿名内部类使用的变量值有没有修改过,当然对于final的编译器就不用检查了,因为是常量嘛。java8之前的版本为了省事,就干脆强制是final的才能给匿名内部类用。
    
    评论
    解决 无用
    打赏 举报
  • 诸相非相 2015-10-22 10:34

    先插一句,java8开始匿名内部类使用的外部变量不再被强制用final修饰。外部变量要么是final的,要么自初始化后值不会被改变,这两种都是可以在匿名内部类中使用且编译通过。否则就编译报错:error: local variables referenced from an inner class must be final or effectively final。
    我觉得类似“final增强内部变量的生存期”这样的说法不完全准确。
    先说一下final的外部变量。匿名内部类编译后,final的外部变量(就是常量了),存储到常量池中的,编译期间,匿名内部类使用到该常量的地方都会被替换为对应的“常量值”,比如使用了外部的String str = "java" , 匿名内部类所有用到str的地方都会被替换为"java" 。这样一来,从字节码级别看,内部类引用外部变量的痕迹被抹得一干二净。
    再说一下非final的外部变量。当一个匿名内部类被创建时,其构造函数是自动生成的,如果有在内部类中使用到外部变量(非final修饰),那么这种外部变量是作为匿名内部类构造函数的参数,传递到内部类中使用的。当然,这种参数传递是一种传值传递(实际上所有java的参数传递都传值的,基本类型自不必说,Object及其子类作为函数参数,实际上传的值,就是对象在内存的存储地址),因此在内部类中对该外部变量的“值”修改是无效的;另外,如果外部变量在外部类中有修改,那么内部类使用该变量时可能值已经不是预期的那个“数”了。所以内部类使用的外部变量,不允许有任何的修改才能避免上述问题。
    所以java编译器会检查匿名内部类使用的变量值有没有修改过,当然对于final的编译器就不用检查了,因为是常量嘛。java8之前的版本为了省事,就干脆强制是final的才能给匿名内部类用。

    评论
    解决 无用
    打赏 举报

相关推荐 更多相似问题