为什么内部类引用的外部变量必须用final修饰(JDK1.8可以不用final修饰, 但外部变量也不可更改, 即相当于隐性的final修饰)
发布日期:2021-07-20 20:53:24 浏览次数:37 分类:技术文章

本文共 2374 字,大约阅读时间需要 7 分钟。

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!

转载地址:https://blog.csdn.net/liao0801_123/article/details/84000867 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:JDK1.8新特性及代码
下一篇:对于Spring中事务的readonly属性理解

发表评论

最新留言

做的很好,不错不错
[***.243.131.199]2024年04月19日 02时47分02秒