为何在匿名内部类中只能问被final修饰的本地变量?
来源:互联网 发布:2017网络婚恋诈骗 编辑:程序博客网 时间:2024/06/06 04:37
我们在一个方法中定义匿名内部类访问方法的本地变量时常常会发现编译时出错,被告知“需要被声明为最终类型",甚是疑惑,于是在网上搜索其原因, 在此作一总结。
局部内部类(在方法内部定义的类)中无法直接访问方法中的局部变量,须修饰其为final
1:在方法内声明的本地变量的生命周期与局部内部类对象的生命周期不一致从而导致了这个问题。前者在一个方法运行结束后就随之被销毁。而后者生命周期的终点却并不在此处,只有当该对象不再被引用时,它才会被GC回收。倘若匿名内部类可以直接访问不被final修饰的本地变量,那么就有可能出现一个奇怪的现象:对象在访问一个已经不存在的变量。
2:我们假设局部内部类中可以直接访问方法中的局部变量,且不需要其为final型。我们知道,在内部类中访问变量实际上是在访问该变量的复制品,如果上述条件成立,无论是基本类型还是引用类型,那么一旦局部变量实体或者复制品任何一方发生改变,都不能相互同步,从而造成变量的实体与复制品不一致,想象一下你看着是在访问一个变量,然而你得到的值却与实际的值不同,因此这样就毫无意义可言。
为了更加清晰深刻的了解Java在处理匿名内部类访问final修饰的本地变量时的处理过程,先看看下面这一段代码
代码清单FinalKeywordTest.java
public class FinalKeywordTest {public static void main(String[] args) {final int i = 10;final Person person = new Person("penny", 26);new Thread() {@Overridepublic void run() {System.out.println(i + 1);System.out.println(person);}};}}class Person{String name;int age;public Person(String name, int age) {super();this.name = name;this.age = age;}@Overridepublic String toString() {return "Person [name=" + name + ", age=" + age + "]";}}
首先通过JDK的工具javap查看一下这个Java文件被编译成什么样子的
javap -v FinalKeywordTest.class > 1.txtjavap -v FinalKeywordTest$1.class > 2.txt
这里得到的结果分别打印到1.txt和2.txt中了,由于我们研究的是匿名内部类如何访问final修饰的本地变量,所以我们主要看2.txt即可
Classfile /D:/Workspace/eclipse-for-temp/test/bin/com/kmter/test/FinalKeywordTest$1.class Last modified 2014-12-21; size 772 bytes MD5 checksum b2d663f92b7895759e9a26f6124ef489 Compiled from "FinalKeywordTest.java"class com.kmter.test.FinalKeywordTest$1 extends java.lang.Thread SourceFile: "FinalKeywordTest.java" EnclosingMethod: #38.#40 // com.kmter.test.FinalKeywordTest.main InnerClasses: #1; //class com/kmter/test/FinalKeywordTest$1 minor version: 0 major version: 51 flags: ACC_SUPERConstant pool: #1 = Class #2 // com/kmter/test/FinalKeywordTest$1 #2 = Utf8 com/kmter/test/FinalKeywordTest$1 #3 = Class #4 // java/lang/Thread #4 = Utf8 java/lang/Thread #5 = Utf8 val$person #6 = Utf8 Lcom/kmter/test/Person; #7 = Utf8 <init> #8 = Utf8 (Lcom/kmter/test/Person;)V #9 = Utf8 Code #10 = Fieldref #1.#11 // com/kmter/test/FinalKeywordTest$1.val$person:Lcom/kmter/test/Person;//这里可以看到被final修饰的Person对象在匿名内部类中直接体现为常量池的一个对象引用,这个对象的引用是从FinalKeywordTest类的main方法中复制过来的 #11 = NameAndType #5:#6 // val$person:Lcom/kmter/test/Person; #12 = Methodref #3.#13 // java/lang/Thread."<init>":()V #13 = NameAndType #7:#14 // "<init>":()V #14 = Utf8 ()V #15 = Utf8 LineNumberTable #16 = Utf8 LocalVariableTable #17 = Utf8 this #18 = Utf8 Lcom/kmter/test/FinalKeywordTest$1; #19 = Utf8 run #20 = Fieldref #21.#23 // java/lang/System.out:Ljava/io/PrintStream; #21 = Class #22 // java/lang/System #22 = Utf8 java/lang/System #23 = NameAndType #24:#25 // out:Ljava/io/PrintStream; #24 = Utf8 out #25 = Utf8 Ljava/io/PrintStream; #26 = Methodref #27.#29 // java/io/PrintStream.println:(I)V #27 = Class #28 // java/io/PrintStream #28 = Utf8 java/io/PrintStream #29 = NameAndType #30:#31 // println:(I)V #30 = Utf8 println #31 = Utf8 (I)V #32 = Methodref #27.#33 // java/io/PrintStream.println:(Ljava/lang/Object;)V #33 = NameAndType #30:#34 // println:(Ljava/lang/Object;)V #34 = Utf8 (Ljava/lang/Object;)V #35 = Utf8 SourceFile #36 = Utf8 FinalKeywordTest.java #37 = Utf8 EnclosingMethod #38 = Class #39 // com/kmter/test/FinalKeywordTest #39 = Utf8 com/kmter/test/FinalKeywordTest #40 = NameAndType #41:#42 // main:([Ljava/lang/String;)V #41 = Utf8 main #42 = Utf8 ([Ljava/lang/String;)V #43 = Utf8 InnerClasses{ com.kmter.test.FinalKeywordTest$1(com.kmter.test.Person); flags: //默认构造函数字节码 Code: stack=2, locals=2, args_size=2 0: aload_0 1: aload_1 2: putfield #10 // Field val$person:Lcom/kmter/test/Person;//将实例域person赋值为常量池中的#10(这里是关键,将main方法中的person对象在本类中认为是一个实例变量) 5: aload_0 6: invokespecial #12 // Method java/lang/Thread."<init>":()V 9: return LineNumberTable: line 1: 0 line 7: 5 LocalVariableTable: Start Length Slot Name Signature 0 10 0 this Lcom/kmter/test/FinalKeywordTest$1;//run方法的字节码 public void run(); flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: getstatic #20 // Field java/lang/System.out:Ljava/io/PrintStream; 3: bipush 11//将单字节常量11压入栈顶 5: invokevirtual #26 // Method java/io/PrintStream.println:(I)V//执行println(int)方法 8: getstatic #20 // Field java/lang/System.out:Ljava/io/PrintStream; 11: aload_0 //将this指针压入栈顶 12: getfield #10 // Field val$person:Lcom/kmter/test/Person;//拿到person对象并将其压入栈顶 15: invokevirtual #32 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V//执行println(Object)方法 18: return LineNumberTable: line 11: 0 line 12: 8 line 13: 18 LocalVariableTable: Start Length Slot Name Signature 0 19 0 this Lcom/kmter/test/FinalKeywordTest$1;}
通过上面的字节码分析我们可以发现匿名内部类访问本地变量的时候是直接将本地对象的引用当作了该类中的一个实例变量来处理的,因为是由final修饰,所以不用担心指针发生更改
- 为何在匿名内部类中只能问被final修饰的本地变量?
- 为什么匿名内部类只能访问final修饰的变量
- 局部内部类只能访问局部中被final修饰的变量
- JAVA中内部类(匿名内部类)访问的局部变量为什么要用final修饰?
- JAVA中内部类(匿名内部类)访问的局部变量为什么要用final修饰?
- 局部内部类只能访问方法内被final修饰的局部变量
- 关于java为什么在Method中局部内部类或者匿名内部类调用Method中定义的变量要用final修饰?
- 注意java8中已经没有匿名内部类和局部内部类只能访问final变量的限制了!
- “局部内部类只能访问被final修饰的局部变量”的解释
- 于java里方法的内部类只能访问被final修饰的局部变量和.
- 局部内部类只能访问被final修饰的局部变量
- 为什么局部内部类和匿名内部类只能访问final的局部变量?
- Java内部类详解 及 局部内部类和匿名内部类只能访问局部final变量的原因
- 内部类——为什么匿名内部类和局部内部类只能访问final变量
- 局部内部类为什么只能访问final修饰的局部变量
- 为什么方法中的内部类只能访问final修饰的局部变量
- 匿名内部类使用外部的局部变量时为什么一定要final修饰
- 方法中的匿名内部类只能访问final类型的局部变量
- linux下解压命令大全
- 手机网络游戏框架体系要点分析
- 黑马程序员_Java编程基础(二)
- [Android] ADT关联源码与API文档 [2013-06-26更新]
- C++著名程序库的比较和学习经验
- 为何在匿名内部类中只能问被final修饰的本地变量?
- 上兴远控原理疑问
- readn writen实现linux下socket缓冲区读写
- 堆栈的区别
- OAuth2.0简介(QQ登录)
- javaSocket与C通信
- MongoDB<一>基础入门
- quadratic polynomial --glossary
- struts2标签库使用小结