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

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

为何匿名内部类访问外部变量要用final

评分

参与人数 1黑马币 +6 收起 理由
李培根 + 6 神马都是浮云

查看全部评分

11 个回复

倒序浏览

回帖奖励 +5

是变量的作用域的问题,因为匿名内部类是出现在一个方法的内部的,如果它要访问这个方法的参数或者方法中定义的变量,则这些参数和变量必须被修饰为final。因为虽然匿名内部类在方法的内部,但实际编译的时候,内部类编译成Outer.Inner,这说明内部类所处的位置和外部类中的方法处在同一个等级上,外部类中的方法中的变量或参数只是方法的局部变量,这些变量或参数的作用域只在这个方法内部有效。因为编译的时候内部类和方法在同一级别上,所以方法中的变量或参数只有为final,内部类才可以引用。

评分

参与人数 1技术分 +1 收起 理由
Rancho_Gump + 1

查看全部评分

回复 使用道具 举报

回帖奖励 +5

楼上说的好,学习了....
回复 使用道具 举报

回帖奖励 +5

生命周期问题,外部变量存在于栈内存中,他可能随着方法的执行后就已不在存在,不存在的话,内部类访问这个不存在的变量就会出错,
回复 使用道具 举报

回帖奖励 +5

二楼的很详细,学习···
回复 使用道具 举报

回帖奖励 +5

  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:07
7#

回帖奖励 +5

我查了查相关资料,理解如下:在java中,方法是一种状态,是不能被存储的,对象才是真正被存储在堆内存中的。匿名内部类是在内部,是局部的,它所处的外部环境即方法,在执行完后就不存在了,但是内部类作为一个对象,是有可能被存储下来的。而局部变量也是如此,局部变量时存在于栈内存中的,使用完了就立即被消灭掉了,而匿名内部类访问局部变量,也是对它的一个拷贝而已;那么试想,局部变量都没了,匿名内部类还存在,这个匿名内部类要去哪找这个变量啊,她找不到局部变量这孩子,多着急,只能让虚拟机报警了(编译失败)。所以就要将这个局部变量变为永久的常量才行。
回复 使用道具 举报
炉海佳 来自手机 高级黑马 2013-2-17 15:16:21
8#

回帖奖励 +5

学习了----
回复 使用道具 举报

回帖奖励 +5

学习了      
回复 使用道具 举报

回帖奖励 +5

如果定义一个匿名内部类,并且希望它使用一个在其外部定的对象,那么编译器会要求其参数引用是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);
  }
  };
}
}

回复 使用道具 举报

回帖奖励 +5

沙发正解
回复 使用道具 举报
.匿名内部类为什么只能用final.是变量的作用域的问题,因为匿名内部类是出现在一个方法的内部的,如果它要访问这个方法的参数或者方法中定义的变量,则这些参数和变量必须被修饰为final。因为虽然匿名内部类在方法的内部,但实际编译的时候,内部类编译成Outer.Inner,这说明内部类所处的位置和外部类中的方法处在同一个等级上,外部类中的方法中的变量或参数只是方法的局部变量,这些变量或参数的作用域只在这个方法内部有效。因为编译的时候内部类和方法在同一级别上,所以方法中的变量或参数只有为final,内部类才可以引用。
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马