内部类引用外部类的局部变量要用final修饰

为什么内部类引用外部类的局部变量时,此变量要用final修饰
代码

   public void test() {

       final int i = 3;

       runOnUiThread(new Runnable() {
           @Override
           public void run() {
               mTvShow.setText(String.valueOf(i));
           }
       });
   }
  上面的代码是使用了匿名内部类的方式。Runnable是一个接口,此内部类实现了Runnable方法,重写了接口里面的run()方法,方法内引用了外部方法体内的局部变量 i,这个时候i只能被定义为final变量,否则编译会报错。

  我们可以从JVM的角度去解释这个现象,在编译期的时候,所有的类都会被编译成Class文件。匿名内部类也会被编译成Class文件。但是上面的例子的内部类编译会和我们所知道的普通类编译方式会有些不同。 
  大多数的类在编译的时候,需要知道每个方法需要为其所有的局部变量分配多少内存。所以它会去检查方法内定义的变量,从而确定此方法到真正运行的时候需要在栈中开辟多少内存。但这只是计算需要多少内存,真正分配内存是在运行期。 
  所以可以发现匿名内部类不同的是,虽然方法中的i没有定义,但是在编译期会给它分配额外的内存,并给它赋与外部的i同一个值。但是此变量已经不是外部的局部变量了。在内存的角度上看,匿名内部类的i实际上不是外部的i,它们使用的内存空间都不同,只是它们的值相同。 
  其实本质上来说,完全可以当作两个不同的变量去使用,但是Java的设计人员可能想要保持一致性,因为Java的初学者在不了解其中真正的机制的时候,会以为他们就是同一个变量,所以干脆就把变量强制定义为final,这样变量就不能被重新赋值,营造一种他们是同一个变量的“假象”。

为什么引用外部类的成员变量的时候,又不用final修饰呢?
  有一点要注意,当引用的不是局部变量而是外部类的成员变量的时候,不一样要用final修饰,因为它不需要像上面说的那样需要在栈中重新开辟一个空间,而是内部类持有外部类的引用,可以直接引用外部类的成员变量。

与C/C++的不同
  在C++中,同样的现象,引用外部类的局部变量时,是不用加final了。其根本原因是出在,C++在编译期的时候就进行动态连接了,而Java是在运行期的时候才进行动态连接。 
  说白了,就是C++的编译时就已经分配好了空间,自然外部类和匿名内部类的i其实用的是同一块内存区域,是真正意义上的同个变量,所以不需要加final。

猜你喜欢

转载自blog.csdn.net/shida_csdn/article/details/84948212
今日推荐