为什么内部类引用的外部变量必须用final修饰(JDK1.8可以不用final修饰, 但外部变量也不可更改, 即相当于隐性的final修饰)

final 是保护 所修饰的类、方法、变量不能被改变的,并不占用内存。

  真正的原因是因为生命周期的原因。方法中的局部变量,方法结束后这个变量就要释放掉,而final会保证这个变量始终指向一个对象。

  首先,内部类和外部类其实是处于同一个级别,反编译中可以看到都是对象级别的类文件,内部类不会因为定义在方法中就会随着方法的执行完毕而跟随者被销毁。问题就来了,如果外部类的方法中的变量不定义final,那么当外部类方法执行完毕的时候,这个局部变量肯定也就被GC了,然而内部类的某个方法还没有执行完,这个时候他所引用的外部变量已经找不到了。如果定义为final,java会将这个变量复制一份作为成员变量内置于内部类中(反编译class文件中可以看到),这样的话,由于final所修饰的值始终无法改变,所以这个变量所指向的内存区域就不会变。

局部内部类是嵌套在方法和作用域内的,对于这个类的使用主要是应用与解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类,局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法和属性中被使用,出了该方法和属性就会失效。

而匿名内部类也可以说是局部内部类的一种,有时候一个类只使用一次,就可以用匿名内部类,告诉GC只用一次就可以回收了,同时也可以简化代码和方便地定义回调

需要注意的是局部内部类和匿名内部类引用外部变量时,外部的变量需要是final 的:

abstract class InnerClass {
public abstract void print();
}
public class Outer {
public  void test1(final String s1) {// 参数必须是final
    //成员内部类
    InnerClass c = new InnerClass() {
        public void print() {
            System.out.println(s1);
        }
    };
    c.print();
}

public  void test2(final String s2) {// 参数必须是final
    //匿名内部类
     new Outer() { //名字可以跟外部类一样
        public void print() {
            System.out.println(s2);
        }
    }.print();
}    
public static void main(String[] args) {
    Outer o=new Outer();
    o.test1("inner1");
    o.test2("inner2");

}
}

为什么匿名内部类和局部内部类引用外部的变量必要是final的呢?
直接看编译出来的源码吧

InnerClass:

    abstract class InnerClass
    {
      public abstract void print();
    }

Outer.class:

`import java.io.PrintStream;

public class Outer
{
  public void test1(String paramString)
  {
Outer.1 local1 = new InnerClass(this, paramString) {
  public void print() {
    System.out.println(this.val$s1);//引用了s1变量
  }

};
local1.print(); }

  public void test2(String paramString) {
new Outer(this, paramString) {//名字可以一样
  public void print() {
    System.out.println(this.val$s2);
  }
}
.print();
  }

 public static void main(String[] paramArrayOfString)
  {
Outer localOuter = new Outer();
localOuter.test1("inner1");
localOuter.test2("inner2");
  }
}

局部内部类Outer$1.class:

import java.io.PrintStream;
class 1 extends InnerClass
{
  public void print()
  {
    System.out.println(this.val$s1);//引用了s1变量
  }
}

匿名内部类Outer$2.class:

import java.io.PrintStream;
class 2 extends Outer
{
  public void print()
  {
    System.out.println(this.val$s2);//引用了s2变量
  }
}

看源码两个内部类各编译出了一个独立的class文件,也就是说Outer$1和Outer$2的生命周期是对象级别的,而变量s1、s2是方法级别的,方法运行完了变量就销毁了,而局部内部类对象Outer$1和Outer$2还可能一直存在(只能没有人再引用该对象时,它才会被GC回收),它不会随着方法运行结束就马上死亡。这时可能会出现了一种"荒唐"的结果:局部内部类对象inner_object要访问一个已不存在的局部变量s1、s2!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值