黑马程序员技术交流社区

标题: 为何匿名内部类访问外部变量要用final [打印本页]

作者: 沙漠里的小鱼    时间: 2013-2-17 01:23
标题: 为何匿名内部类访问外部变量要用final
为何匿名内部类访问外部变量要用final
作者: 罗正荣    时间: 2013-2-17 01:45
是变量的作用域的问题,因为匿名内部类是出现在一个方法的内部的,如果它要访问这个方法的参数或者方法中定义的变量,则这些参数和变量必须被修饰为final。因为虽然匿名内部类在方法的内部,但实际编译的时候,内部类编译成Outer.Inner,这说明内部类所处的位置和外部类中的方法处在同一个等级上,外部类中的方法中的变量或参数只是方法的局部变量,这些变量或参数的作用域只在这个方法内部有效。因为编译的时候内部类和方法在同一级别上,所以方法中的变量或参数只有为final,内部类才可以引用。
作者: Gaara    时间: 2013-2-17 01:56
楼上说的好,学习了....
作者: 孙含庆    时间: 2013-2-17 03:23
生命周期问题,外部变量存在于栈内存中,他可能随着方法的执行后就已不在存在,不存在的话,内部类访问这个不存在的变量就会出错,
作者: 逍林游    时间: 2013-2-17 09:26
二楼的很详细,学习···
作者: 王亚东    时间: 2013-2-17 09:38
  1. package bao01.io;

  2. import java.io.FileInputStream;
  3. import java.io.IOException;
  4. import java.util.ArrayList;
  5. import java.util.Enumeration;
  6. import java.util.Iterator;

  7. public class T08_MergerIO {

  8.         // 获得Enumera对象
  9.         public  Enumeration<FileInputStream> getEn() throws IOException {
  10.                 ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();
  11.                 for (int i = 1; i <= 3; i++) {
  12.                         al.add(new FileInputStream("d:\\" + i + ".part")); // 假如d盘下有1.part、2.part、3.part三个文件

  13.                 }
  14.                 final Iterator<FileInputStream> iterator = al.iterator();
  15.                 for (; iterator.hasNext();) {
  16.                         System.out.println(iterator.next());
  17.                 }

  18.                 Enumeration<FileInputStream> en = new Enumeration<FileInputStream>() {
  19.                         @Override
  20.                         public FileInputStream nextElement() {
  21.                                 return iterator.next();
  22.                         }

  23.                         public boolean hasMoreElements() {
  24.                                 return iterator.hasNext();
  25.                         }
  26.                 };
  27.                 return null;
  28.         }

  29.         public static void main(String[] args) throws IOException {
  30.                 T08_MergerIO t = new T08_MergerIO();
  31.                 t.getEn(); // 如果iterator不被final修饰,第一次调用iterator被初始化,匿名类中的iterator也是这个iterator
  32.                 Enumeration<FileInputStream> en = t.getEn();        //第二次iterator将被重置。而匿名类中的iterator原本指向的堆中的值将失去引用,
  33.                 //从而被垃圾回收机制清理。当在下面的代码中使用的时候,就会出现不可预知的错误。
  34.                 // SequenceInputStream sis = new SequenceInputStream(en);
  35.                 //另:被final修饰的变量 会比普通变量有更长的生命周期。
  36.                
  37.                 //注:我是在看黑马程序员基础视频IO——3第20天17讲遇到的这个问题,查了下资料,个人理解至此,希望对大家有所帮助。
  38.                 //另2:方法中的匿名类,在方法被多次调用时,此类会不会被多次编译?不会?
  39.         }

  40. }
复制代码

作者: 黄玉昆    时间: 2013-2-17 11:02
我查了查相关资料,理解如下:在java中,方法是一种状态,是不能被存储的,对象才是真正被存储在堆内存中的。匿名内部类是在内部,是局部的,它所处的外部环境即方法,在执行完后就不存在了,但是内部类作为一个对象,是有可能被存储下来的。而局部变量也是如此,局部变量时存在于栈内存中的,使用完了就立即被消灭掉了,而匿名内部类访问局部变量,也是对它的一个拷贝而已;那么试想,局部变量都没了,匿名内部类还存在,这个匿名内部类要去哪找这个变量啊,她找不到局部变量这孩子,多着急,只能让虚拟机报警了(编译失败)。所以就要将这个局部变量变为永久的常量才行。
作者: 炉海佳    时间: 2013-2-17 15:16
学习了----
作者: 郭冰川    时间: 2013-2-17 15:41
学习了      
作者: 李洪因    时间: 2013-2-17 20:19
如果定义一个匿名内部类,并且希望它使用一个在其外部定的对象,那么编译器会要求其参数引用是final 的。Java虚拟机的实现方式是,编译器会探测局部内部类中是否有直接使用外部定义变量的情况,如果有访问就会定义一个同类型的变量,然后在构造方法中用外部变量给自己定义的变量赋值。

interface aa
{
public void print();
}
public class ww
{
String string="aaaa";          //这里不需要定义final

public static void main(String[] args)
{
}
public void bb()
{
final String string1="bbbb";//这里需要定义为final

aa a1=new aa()
  {
  public void print()
  {
   System.out.println(string);
   System.out.println(string1);
  }
  };
}
}


作者: 刘军亭    时间: 2013-2-17 20:36
沙发正解
作者: 冯飞    时间: 2013-2-20 18:17
.匿名内部类为什么只能用final.是变量的作用域的问题,因为匿名内部类是出现在一个方法的内部的,如果它要访问这个方法的参数或者方法中定义的变量,则这些参数和变量必须被修饰为final。因为虽然匿名内部类在方法的内部,但实际编译的时候,内部类编译成Outer.Inner,这说明内部类所处的位置和外部类中的方法处在同一个等级上,外部类中的方法中的变量或参数只是方法的局部变量,这些变量或参数的作用域只在这个方法内部有效。因为编译的时候内部类和方法在同一级别上,所以方法中的变量或参数只有为final,内部类才可以引用。




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