这里的局部变量就是在类方法中的变量,能访问方法中变量的类当然也是局部内部类了。
我们都知道,局部变量在所处的函数执行完之后就释放了,但是内部类对象如果还有引用指向的话它是还存在的。例如下面的代码:
-
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就声明了这个引用就只能指着这一个对象了。这样就保证了内外统一。
================================================
从上面的三个文章看出,主要问题是拷贝导致的。第二种解释,只考虑到了一个变量的拷贝,就是局部内部类的拷贝。
但我们看反编译代码:
KaTeX parse error: Expected '}', got 'EOF' at end of input: …1; this.valdog = paramDog;
}
" data-snippet-id=“ext.fe01fbf0a0ee0ded4b69f44ccf6de904” data-snippet-saved=“false” data-codota-status=“done”>
-
private Dog val d o g ; < / d i v > < / d i v > < / l i > < l i > < d i v c l a s s = " h l j s − l n − n u m b e r s " > < d i v c l a s s = " h l j s − l n − l i n e h l j s − l n − n " d a t a − l i n e − n u m b e r = " 2 " > < / d i v > < / d i v > < d i v c l a s s = " h l j s − l n − c o d e " > < d i v c l a s s = " h l j s − l n − l i n e " > < s p a n c l a s s = " h l j s − k e y w o r d " > p r i v a t e < / s p a n > A n o n y m o u s D e m o 1 m y A n o n y m o u s D e m o 1 ; < / d i v > < / d i v > < / l i > < l i > < d i v c l a s s = " h l j s − l n − n u m b e r s " > < d i v c l a s s = " h l j s − l n − l i n e h l j s − l n − n " d a t a − l i n e − n u m b e r = " 3 " > < / d i v > < / d i v > < d i v c l a s s = " h l j s − l n − c o d e " > < d i v c l a s s = " h l j s − l n − l i n e " > < / d i v > < / d i v > < / l i > < l i > < d i v c l a s s = " h l j s − l n − n u m b e r s " > < d i v c l a s s = " h l j s − l n − l i n e h l j s − l n − n " d a t a − l i n e − n u m b e r = " 4 " > < / d i v > < / d i v > < d i v c l a s s = " h l j s − l n − c o d e " > < d i v c l a s s = " h l j s − l n − l i n e " > A n o n y m o u s D e m o 1 dog;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">private</span> AnonymousDemo1 myAnonymousDemo1;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> AnonymousDemo1 dog;</div></div></li><li><divclass="hljs−ln−numbers"><divclass="hljs−ln−linehljs−ln−n"data−line−number="2"></div></div><divclass="hljs−ln−code"><divclass="hljs−ln−line"><spanclass="hljs−keyword">private</span>AnonymousDemo1myAnonymousDemo1;</div></div></li><li><divclass="hljs−ln−numbers"><divclass="hljs−ln−linehljs−ln−n"data−line−number="3"></div></div><divclass="hljs−ln−code"><divclass="hljs−ln−line"></div></div></li><li><divclass="hljs−ln−numbers"><divclass="hljs−ln−linehljs−ln−n"data−line−number="4"></div></div><divclass="hljs−ln−code"><divclass="hljs−ln−line">AnonymousDemo1 1(AnonymousDemo1 paramAnonymousDemo1, Dog paramDog) {
-
this.myAnonymousDemo1 = paramAnonymousDemo1;
-
this. val$dog = paramDog;
-
}
实际上是有两份拷贝的,一份是第二种解释中的dog的拷贝。其实,还有一个是外部类 paramAnonymousDemo1
的拷贝。如果不拷贝的情况下,这个dog的拷贝数据应该是谁的呢,是属于外部类的对象的。而现在两个都被进行了拷贝。这会导致两种类型的不一致。一: 外部类引用已经引用指向为null,而实际在内部类中使用的拷贝还存在。这就是第一种说的生命周期不一致问题。第二种解释的比较清楚了。就是局部变量的拷贝会导致访问上的不一致
总结:无论是第一种生命周期不一致,还是说因为修改引用导致不一致,其实本质上都是因为拷贝导致的。但是由两种拷贝1,外部类对象的拷贝。2.局部变量的拷贝 。两种拷贝分别导致上面的两种问题。第一种:局部内部类访问局部变量的时候,首先会拷贝一份外部类对象使用的是外部类的一份拷贝,这份拷贝的原本如果不存在了,那么就不会访问到局部变量。第二种其实忽略了一点,就是,拷贝不仅是dog对象的拷贝,也就是局部内部类的对象有拷贝,而且,外部类对象,其实也是一个拷贝,而这个拷贝在存在的过程中,可能会出现外面的原本已经不存在了。当这个拷贝跟原本生命周期出现不一致的时候,自然会出现垃圾回收的问题或者访问上的不一致。