Java进阶 ——— 局部内部类访问局部变量为何必须加final关键字

疑问

在Java中,局部内部类若是调用了方法中的变量,那么该变量必须申明为final类型,若是不申明,则编译就会出错。java

这里的内部类指的是方法内部类或匿名内部类,不包含静态内部类和成员内部类bash

这里经过一个例子类分析函数

public class InnerClass {
  
	private int defaultAge = 5;
  局部变量 age,必须添加final关键字,这里先不加
	public void addAge( int age){
  
    //局部内部类
		class NewAge{
			private int getAge(){
				return age + defaultAge;
			}
		}

		NewAge newAge = new NewAge();
		System.out.print(newAge.getAge());
	}
}
复制代码

强行不加final,编译,则会报错:ui

Error:(16, 12) 错误: 从内部类中访问本地变量age; 须要被声明为最终类型this

分析

  • 缘由

1.生命周期不一样: 为何必须局部变量加final关键字呢?由于局部变量直接存储在栈中,当方法执行结束,非final的局部变量就被销毁,而局部内部类对局部变量的引用依然存在,当局部内部类要调用局部变量时,就会出错,出现非法引用。简单来讲,就是非final的局部变量的生命周期比局部内部类的生命周期短,是否是直接能够拷贝变量到局部内部类?这样内部类中就可使用并且不担忧生命周期问题呢?也是不能够的,由于直接拷贝又会出现第二个问题,就是数据不一样步 2.数据不一样步:内部类并非直接使用传递进来的参数,而是将传递进来的参数经过本身的构造器备份到本身内部,表面看是同一个变量,实际调用的是本身的属性而不是外部类方法的参数,若是在内部类中,修改了这些参数,并不会对外部变量产生影响,仅仅改变局部内部类中备份的参数。可是在外部调用时发现值并无被修改,这种问题就会很尴尬,形成数据不一样步。因此使用final避免数据不一样步的问题spa

  • 原理

那为何添加final修饰的局部变量,就能够被局部内部类引用呢? 若定义为final,则java编译器则会在内部类NewAge内生成一个外部变量的拷贝,并且能够既能够保证内部类能够引用外部属性,又能保证值的惟一性 也就是拷贝了一个变量的副本,提供给局部内部类,这个副本的生命周期和局部内部类同样长,而且这个副本不能够修改,保证了数据的同步 注意在Java8 中,被局部内部类引用的局部变量,默认添加final,因此不须要添加final关键词.net

  • 字节码

若是有兴趣,能够看看编译后的字节码,即.class文件code

延伸 如何查看Java字节码blog

class InnerClass$1NewAge {
    //能够看到,局部内部类中的使用的age,是经过构造函数传递进来,并非直接引用外部变量。
    InnerClass$1NewAge(InnerClass var1, int var2) {
        this.this$0 = var1;
        this.val$age = var2;
    }

    private int getAge() {
        return this.val$age + InnerClass.access$000(this.this$0);
    }
}

复制代码

InnerClass类编译后,在文件夹会出现InnerClass.class和InnerClass$1NewAge.class,这说明外部类的方法 和内部类处于同一级。生命周期

结论

局部内部类引用局部变量,不添加final,会出现生命周期不一样,致使非法引用问题,并且直接拷贝会出现数据不一样步问题,因此使用final,保证了合法引用,并且数据不可修改

相关文章
相关标签/搜索