匿名内部类使用的外部变量为什么要是final的
来源:互联网 发布:淘宝买射钉枪警察找我 编辑:程序博客网 时间:2024/05/16 17:57
除了匿名内部类内部,方法和作用域内的内部类内部使用的外部变量也必须是 final 的。原因如下:
内部类会自动拷贝外部变量的引用,为了避免:
1.外部方法修改引用,而导致内部类得到的引用值不一致
2.内部类修改引用,而导致外部方法的参数值在修改前和修改后不一致。于是就用 final 来让该引用不可改变。
作者:周鹏
链接:https://www.zhihu.com/question/21395848/answer/39841533
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
下面文章转载自http://blog.csdn.net/u011617742/article/details/51613519
Java中的匿名内部类是如何实现的?
先定义一个接口:
123
然后创建这个接口的匿名子类:
12345678910111213141516
这个匿名子类会被编译成一个单独的类,反编译的结果是这样的:
1234567891011121314
可以看到外部类和名为number的局部变量是作为构造方法的参数传入匿名内部类的
2,为什么局部变量要作为内部类构造方法的参数传入?
为了解决:局部变量的生命周期与局部内部类的对象的生命周期的不一致性问题
(1)问题:设方法useMyInterface被调用,从而在它的调用栈中生成了变量number,此时产生了一个局部内部类对象myInterface,它访问了该局部变量number .
当方法useMyInterface运行结束后,局部变量number就已死亡了,不存在了.
但局部内部类对象myInterface还可能一直存在(只能没有人再引用该对象时,它才会死亡),它不会随着方法useMyInterface运行结束死亡.这时出现了一个"荒唐"结果:局部内部类对象myInterface要访问一个已不存在的局部变量number
(2)解决方法:通过将局部变量"复制"一份,复制品直接作为局部内部类的数据成员.这样:当局部内部类访问局部变量 时,其实真正访问的是这个局部变量的"复制品"(即:这个复制品就代表了那个局部变量).因此:当运行栈中的真正的局部变量死亡时,局部内部类对象仍可以 访问局部变量(其实访问的是"复制品"),给人的感觉:好像是局部变量的"生命期"延长了.
(3)为什么会与final有关系?
为了保证局部变量和 内部类中复制品 的数据一致性。
若是基本数据类型,当变量是final时,由于其值不变,因而:其复制品与原始的量是一样.语义效果相同
若:不是final,就无法保证:复制品与原始变量保持一致了,因为:在方法中改的是原始变量,而局部内部类中改的是复制品
若是引用类型,当 变量是final时,由于其引用值不变(即:永远指向同一个对象),因而:其复制品与原始的引用变量一样,永远指向同一个对象(由于是 final,从而保证:只能指向这个对象,再不能指向其它对象),达到:局部内部类中访问的复制品与方法代码中访问的原始对象,永远都是同一个即:语义效 果是一样的
.否则:当方法中改原始变量,而局部内部类中改复制品时,就无法保证:复制品与原始变量保持一致了(因此:它们原本就应该是同一个变量.)
3,例子
现在我们来看,如果我要实现一个在一个方法中匿名调用ABSClass的例子:
public static void test(final String s){
//或final String s = "axman";
ABSClass c = new ABSClass(){
public void m(){
int x = s.hashCode();
System.out.println(x);
}
};
//其它代码.
}
从代码上看,在一个方法内部定义的内部类的方 法访问外部方法内局部变量或方法参数,是非常自然的事,但内部类编译的时候如何获取这个变量,因为内部类除了它的生命周期是在方法内部,其它的方面它就是 一个普通类。那么它外面的那个局部变量或方法参数怎么被内部类访问?编译器在实现时实际上是这样的:
public static void test(final String s){
//或final String s = "axman";
class OuterClass$1 extends ABSClass{
private final String s;
public OuterClass$1(String s){
this.s = s;
}
public void m(){
int x = s.hashCode();
System.out.println(x);
}
};
ABSClass c = new OuterClass$1(s);
//其它代码.
}
即外部类的变量被作为构造方法的参数传给了内部类的私有成员.
假如没有final,那么:
public static void test(String s){
//或String s = "axman";
ABSClass c = new ABSClass(){
public void m(){
s = "other";
}
};
System.out.println(s);
}
就会编译成:
public static void test(String s){
//或String s = "axman";
class OuterClass$1 extends ABSClass{
private String s;
public OuterClass$1(String s){
this.s = s;
}
public void m(){
s = "other";
}
};
ABSClass c = new OuterClass$1 (s);
}
内部类的s重新指向"other"并不影响test的参数或外部定义的那个s.同理如果外部的s重新赋值内部类的s也不会跟着改变。
而你看到的
public static void test(String s){
//或String s = "axman";
ABSClass c = new ABSClass(){
public void m(){
s = "other";
}
};
System.out.println(s);
}
在语法上是一个s,在内部类中被改变了,但结果打印的出来的你认为是同一的s却还是原来的"axman",
你能接收这样的结果吗?
所以final从语法上约束了实际上两个不同变量的一致性(表现为同一变量).
4.jdk1.8版本下的新变化
1.8以后,并非不用是final的,而是在编译期间要求值不发生变化。在你的代码中,如果user的值变化了,就会出错。
编译如下代码:
- public class TestFinal
- {
- //这样代码就不能通过编译了
- public void test( User user)//这里不用final User user,但是user不能改变
- {
- user = new User();//user变化了,所以报错
- user.setName("zhaoyang");
- (new Thread()
- {
- public void run()
- {
- System.out.println("user.name-->"+user.name);//报错
- }
- }
- ).start();
- }
- public static void main(String[] args)
- {
- User user=new User();
- user.setId(007);
- user.setName("zhaoyang");
-
- TestFinal testFinal=new TestFinal();
- testFinal.test(user);
- }
- }
- 匿名内部类使用的外部变量为什么要是final
- 匿名内部类使用的外部变量为什么要是final的
- 匿名内部类使用外部的局部变量时为什么一定要final修饰
- 匿名内部类 使用外部的变量为什么要声明成final
- 匿名内部类 使用外部的变量为什么要声明成final
- [Java] 匿名内部类访问外部类的局部变量为什么一定得是final类型
- java匿名内部类使用外部变量时,外部变量必须是final,为什么?
- java匿名内部类使用外部变量时,外部变量必须是final,为什么?
- java匿名内部类使用外部变量时,外部变量必须是final,为什么?
- 为什么匿名内部类中用外部类的成员变量,外部类的成员变量必须是final
- 为什么匿名内部类只能访问final修饰的变量
- 为什么内部类访问的外部变量需要使用final修饰
- 为什么内部类访问的外部变量需要使用final修饰
- 为什么内部类访问的外部变量需要使用final修饰
- 为什么内部类访问的外部变量需要使用final修饰
- JAVA中内部类(匿名内部类)访问的局部变量为什么要用final修饰?
- JAVA中内部类(匿名内部类)访问的局部变量为什么要用final修饰?
- 为什么局部内部类和匿名内部类只能访问final的局部变量?
- 专利:专利说明书的主要组成部分
- 合并二叉排序树
- Linux C编程 第13章进程间通信方式十个问题及解答(2)
- 数据结构与算法--栈与队列
- Concave and Convex Function
- 匿名内部类使用的外部变量为什么要是final的
- POJ 3680 Intervals
- VirtualQueryEx详解
- Matlab—寻找峰值函数介绍
- opencv cuda
- kafka分布式消息队列 — 基本概念介绍
- PHP 引用在线编辑器,kindeditor
- apt() 问题
- 习题8