这里的局部变量就是在类方法中的变量,能访问方法中变量的类当然也是局部内部类了。
我们都知道,局部变量在所处的函数执行完之后就释放了,但是内部类对象如果还有引用指向的话它是还存在的。例如下面的代码:
class Outer{
public static void main(String[] args){
Outer out = new Outer();
Object obj = out.method();
}
Object method(){
int locvar = 1;
class Inner{
void displayLocvar(){
System.out.println("locvar = " + locvar);
}
}
Object in = new Inner();
return in;
}
}
当out.method()方法执行结束后,局部变量 locvar 就消失了,但是在method()方法中 obj in = new Inner() 产生的 in 对象还存在引用obj,这样对象就访问了一个不存在的变量,是不允许的。这种矛盾是由局部内部类可以访问局部变量但是局部内部类对象和局部变量的生命周期不同而引起的。
局部内部类访问局部变量的机制
在java中,类是封装的,内部类也不例外。我们知道,非静态内部类能够访问外部类成员是因为它持有外部类对象的引用 Outer.this, 就像子类对像能够访问父类成员是持有父类对象引用super一样。局部内部类也和一般内部类一样,只持有了Outer.this,能够访问外部类成员,但是它又是如何访问到局部变量的呢?
实际上java是将局部变量作为参数传给了局部内部类的构造函数,而将其作为内部类的成员属性封装在了类中。我们看到的内部类访问局部变量实际上只是访问了自己的成员属性而已,这和类的封装性是一致的。那么上面的代码实际上是这样:
Object method(){
int locvar = 1;
class Inner{
private int obj;
public Inner(int obj){
this.obj = obj;
}
void displayLocvar(){
System.out.println("locvar = " + locvar);
}
}
Object in = new Inner(locvar); //将locvar作为参数传给构造,以初始话成员
return in;
}
那么问题又来了,我们写代码的目的是在内部类中直接控制局部变量和引用,但是java这么整我们就不高兴了,我在内部类中整半天想着是在操作外部变量,结果你给整个副本给我,我搞半天丫是整我自己的东西啊?要是java不这么整吧,由破坏了封装性--------你个局部内部类牛啊,啥都没有还能看局部变量呢。这不是java风格,肯定不能这么干。这咋整呢?
想想,类的封装性咱们一定是要遵守的,不能破坏大局啊。但又要保证两个东西是一模一样的,包括对象和普通变量,那就使用final嘛,当传递普通变量的之前我把它变成一个常量给你,当传递引用对象的时候加上final就声明了这个引用就只能指着这一个对象了。这样就保证了内外统一。
原因:
一, 当方法被调用运行完毕之后,局部变量就已消亡了。但内部类对象可能还存在,
直到没有被引用时才会消亡。此时就会出现一种情况,就是内部类要访问一个不存在的局部变量。
二,解决这一问题的办法就是使用final修饰局部变量,通过将final局部变量"复制"一份,
复制品直接作为方法内部类中的数据成员,这事方法内部类访问的其实是这个局部变量的复制品!
而且,由于被final修饰的变量赋值后不能再修改,所以就保证了复制品与原始变量的一致。
三,原因二的功能能实现的原因是:Java采用了一种copy local variable(复制局部变量)的方式来实现,
也就是说把定义为final的局部变量拷贝过来用,而引用的也可以拿过来用,只是不能重新赋值。
从而造成了可以access local variable(访问局部变量)的假象,而这个时候由于不能重新赋值,
所以一般不会造成不可预料的事情发生。
四, 使用final修饰符不仅会保持对象的引用不会改变,
而且编译器还会持续维护这个对象在回调方法中的生命周期.
所以这才是final变量和final参数的根本意义.