请先看代码:
static List intArrayAsList(final int[] a) {
return new AbstractList() {
public Object get(int i) {
return new Integer(a[i]);
}
public int size() {
return a.length;
}
public Object set(int i, Object o) {
int oldVal = a[i];
a[i] = ((Integer)o).intValue();
return new Integer(oldVal);
}
};
}
public static void main(String[] args) {
int[] a = {0, 1};
List l = intArrayAsList(a);
int b = (int)l.get(0);
}
我的问题在于:当intArrayAsList方法结束时,方法入参不是生命周期就结束了吗?而且在new AbstractList时,也并没有把方法入参传给AbstractList的instance里去。那么当调用l.get()时,到底是从哪里得到的数据?
求详解这部分知识。
匿名内部类在编译时会生成一个普通的类,这样就可以保证该类在方法结束后依然可以使用,传入的final参数会成为该类的某个字段的拷贝,比如你这里传入的是int类型的数组,那么在编译后生成的类中也会有一个类似
final int[]b 的成员,该成员的值就是你传入的a的值。这样保证了当你调用匿名内部类的方法时,能够获取到外部传入的参数值。
在java中当你new一个对象的时候(相当于C/C++中malloc分配一个内存区域),而你返回这个对象时,相当于把这个内存块的地址返回了,在java中变量只是一个引用
匿名内部类中当变量是final时,通过将final局部变量"复制"一份,复制品直接作为局部内部中的数据成员.这样:当局部内部类访问局部变量 时,其实真正访问的是这个局部变量的"复制品"(即:这个复制品就代表了那个局部变量).因此:当运行栈中的真正的局部变量死亡时,局部内部类对象仍可以 访问局部变量(其实访问的是"复制品"),给人的感觉:好像是局部变量的"生命期"延长了.
若是引用类型,由于其引用值不变(即:永远指向同一个对象),因而:其复制品与原始的引用变量一样,永远指向同一个对象(由于是 final,从而保证:只能指向这个对象,再不能指向其它对象),达到:局部内部类中访问的复制品与方法代码中访问的原始对象,永远都是同一个即:语义效 果是一样的.否则:当方法中改原始变量,而局部内部类中改复制品时,就无法保证:复制品与原始变量保持一致了(因此:它们原本就应该是同一个变量.)
也就是匿名内部类识别自动拷贝了一直指向数组a的一个地址。
匿名类实例化后生成了对象在堆里,返回引用给指定的变量
楼主的理解本身没有错,确实intArrayAsList函数执行完,它的参数就销毁了。
1楼的理解基本正确。intArrayAsList的参数int[] a的引用会复制一份到匿名类中,当做匿名类的成员变量,同样也是final的,所以匿名类内都可以访问这个复制的引用。
为什么匿名类用外面函数的变量,一定要求变量为final的呢?有人解释是说final的变量才会复制到匿名类中。既然是复制,那么非final的难道就不能复制吗?应该是可以复制的,只不过非final的复制过来,可能会被改变,从程序运行结果上看,可能不是我们所期望的。
这个问题你可以使用javap命令来查看一下该类的定义。比如外部类是InnerClassDemo.java,里面的内容如你题目所述,编译后会生成InnerClassDemo.class和InnerClassDemo$1.class(受不同的编译器影响$后面的数字会有所不同),执行下面的命令:
javap -private InnerClassDemo$1
得到的结果如下所示,可以看到该匿名内部类的定义,里面包含了带有int[]参数的构造方法,以及存放该参数的变量,也就是会进行备份了。需要注意的是只会备份你用到的参数,没有用的Java很聪明的帮你过滤了。