为什么内部类只能访问外部类中的 final型的常量

yuyang2007 2007-08-08 04:31:02
我在java中的一个类中定义了一个内部类,为什么在内部类中只能够访问外部类中的常量呢,即final 常量?
...全文
1697 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
fishriver 2011-02-27
  • 打赏
  • 举报
回复
匿名内部类使用所在方法传入的参数为什么要求使用final类型呢?

我们仔细研究下stack中随着方法调用的变化。
每个执行中的方法在内存stack中有一个专用的frame,ebp指向开始处,esp指向正在执行处。
方法的局部变量时直接存储在frame中的,而全局变量、final是存储在heap中。
当在这个frame中调用其他方法时(包括自己或其他对象的方法),会压栈,push ebp;生成新的frame。
要把前一个frame中的局部变量传递给新frame,一般使用的是相对位置。例如在新frame中使用上一个frame的变量会使用ebp-4,ebp-8的地址代替。

好了,在正常的调用过程中,都是一个frame,接着一个frame的,参数也是一个接一个传递的。
比如对象A,对象B,对象C,那么A调B,B调C,参数是A传到B,B传到C,可以很正常的通过相对位置找到这些参数。
你想想看,如果是使用匿名内部类,A中创建一个匿名类C,把参数传给他,这个时候并没有真正执行C的方法,你让C怎么记录这个参数地址?然后把这个匿名内部类放在B的方法中,B在调用C,调用过程没有变,A调B,B调C,但参数传递过程变了,A给C,跨了一个frame,由于C不知道B的frame有多长,所以它不可能通过相对位置找到A中的局部变量。


johncarmark 2008-01-21
  • 打赏
  • 举报
回复 1
如果定义一个局部内部类,并且局部内部类使用了一个在其外部定义的对象,为什么编译器会要求其参数引用是final呢?
注意:局部内部类,包括匿名内部类。

原因如下:

abstract class ABSClass{
public abstract void print();
}

public class Test2{
public static void test(final String s){//一旦参数在匿名类内部使用,则必须是final
ABSClass c=new ABSClass(){
public void print(){
System.out.println(s);
}
};
c.print();
}
public static void main(String[] args){
test("Hello World!");
}
}

JVM中每个进程都会有多个根,每个static变量,方法参数,局部变量,当然这都是指引用类型.基础类型是不能作为根的,根其实就是一个存储地址.垃圾回收器在工作时先从根开始遍历它引用的对象并标记它们,如此递归到最末梢,所有根都遍历后,没有被标记到的对象说明没有被引用,那么就是可以被回收的对象(有些对象有finalized方法,虽然没有引用,但JVM中有一个专门的队列引用它们直到finalized方法被执行后才从该队列中移除成为真正没有引用的对象,可以回收,这个与本主题讨论的无关,包括代的划分等以后再说明).这看起来很好.

但是在内部类的回调方法中,s既不可能是静态变量,也不是方法中的临时变量,也不是方法参数,它不可能作为根,在内部类中也没有变量引用它,它的根在内部类外部的那个方法中,如果这时外面变量s重指向其它对象,则回调方法中的这个对象s就失去了引用,可能被回收,而由于内部类回调方法大多数在其它线程中执行,可能还要在回收后还会继续访问它.这将是什么结果?

而使用final修饰符不仅会保持对象的引用不会改变,而且编译器还会持续维护这个对象在回调方法中的生命周期.所以这才是final变量和final参数的根本意义.
vlinux 2008-01-21
  • 打赏
  • 举报
回复
如果这时外面变量s重指向其它对象,则回调方法中的这个对象s就失去了引用,可能被回收
----------------------------------------------------------------------
这点我不同意,因为如果真的允许这样的机制存在,那么回调方法被其他线程引用过去了,这个时候对象依附于回调的方法而被调用回调的其他线程所引用,所以该对象仍旧会被表示为被引用,而不会被GC掉。

我觉得之所以 “内部对象不能使用该内部类所在方法的局部变量,除非该变量为final” 这句话想要避免的错误是:
我们都知道,当一个方法结束的时候,其栈结构就会被销毁。但是从方法中创建的对象仍然可以在堆中。也就是说局部变量的寿命与方法本地内部类对象寿命不一定是一样的长,如果方法本地内部类依赖了方法的一个局部变量,那么当方法结束的时候,本地内部类却依然存活在对象堆之中,但是却找不到方法的局部变量了--随着方法的结束,其堆结构就挂菜了,该局部变量也就没了。为啥final就可以呢,哪是因为final变量的寿命要大于方法的寿命啦,呵呵。

方法的参数表面上看起来是从外面传递进来的,其实本质上就是一个局部的变量,所以也和普通的局部变量一样,必须设置成final才能被方法本地内部类访问。
That's all.
猪小烧 2007-08-09
  • 打赏
  • 举报
回复
规定来的吧
zdjray 2007-08-08
  • 打赏
  • 举报
回复
你说的东西其实比较难理解真正的用意

你说的应该是局部内部类
简单的来说是作用域的问题
就好像方法外面做的事情并不能改变方法内才定义的变量
因为你并不知道方法里面这个时候已经存在了这个局部变量了没有

而你说的情况是只有当要使用的变量是方法里面的变量,而且是在此方法内定义的类才有此限制
这个时候因为在这个内部类中方法里面的本地变量是失效的,也就是不在作用域内,所以是不能够访问的

但是为什么这里用final却又可以访问呢?
因为Java采用了一种copy local variable 的方式来实现,也就是说把定义为final的局部变量拷贝过来用,而引用的也可以拿过来用,只是不能重新赋值。从而造成了可以access local variable的假象

而这个时候由于不能重新赋值,所以一般不会造成不可预料的事情发生

其中的细节很难说清楚,需要求教语言设计者
yczz 2007-08-08
  • 打赏
  • 举报
回复
这是SUN公司为了安全着想
prettyboy07 2007-08-08
  • 打赏
  • 举报
回复
因为你写的是局部内部类,所以只能访问外部类final的局部变量

62,623

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧