Java中为什么内部类对外部类局部变量有引用时,外部类局部变量一定要有final?
2012-09-17 10:14
369 查看
Java中为什么内部类对外部类局部变量有引用时,外部类局部变量一定要有final?
我们来看看Java对内部类的实现。
使用内部类经常是用在Listener这样的。而Listener中的监听函数被调用时,大部分情况,new内部类所在的函数早就执行完了,此时外部类局部变量(保存在栈中)早就因为函数的退出而失效了。
而加上final后,编译器是这样处理内部类的。如果这个外部局部变量是常量,则在内部类代码中直接用这个常量。如果是类的实例,则编译器将产生一个内部类的构造参数,将这个final变量传到内部类里,这样即使外部局部变量无效了,还可以使用。
例如:
viewsource
print?
使用javap-privateTest$1B(这个就是内部类)将看到
Compiledfrom"Test.java"classTest$1Bextendsjava.lang.Object{
publicintc;privatefinaljava.util.Listval$a;privatefinalTestthis$0;Test$1B(Test,java.util.List);publicvoidrun();}而下面是使用常量的例子:
viewsource
print?
javap-private-cTest$1B:
Compiledfrom"Test.java"classTest$1Bextendsjava.lang.Object{
publicintc;privatefinalTestthis$0;Test$1B(Test);Code:
0:aload_0
1:invokespecial#1;//Methodjava/lang/Object."<init>":()V
4:aload_0
5:aload_1
6:putfield#2;//Fieldthis$0:LTest;9:return
publicvoidrun();Code:
0:bipush100
2:istore_1
3:getstatic#3;//Fieldjava/lang/System.out:Ljava/io/PrintStream;6:iload_1
7:invokevirtual#4;//Methodjava/io/PrintStream.println:(I)V
10:return
}另外引用别人的一段话:
内部类并不是直接调用方法传进来的参数,而是内部类将传进来的参数通过自己的构造器备份到了自己的内部,自己内部的方法调用的实际是自己的属性而不是外部类方法的参数。
这样理解就很容易得出为什么要用final了,因为两者从外表看起来是同一个东西,实际上却不是这样,如果内部类改掉了这些参数的值也不可能影响到原参数,然而这样却失去了参数的一致性,因为从编程人员的角度来看他们是同一个东西。
如果我们不强制使用final的话,那么编程人员在程序设计的时候在内部类中改掉参数的值,但是外部调用的时候又发现值其实没有被改掉,这就让人非常的难以理解和接受,为了避免这种尴尬的问题存在,所以编译器设计人员把内部类能够使用的参数设定为必须是final来规避这种莫名其妙错误的存在。
我们来看看Java对内部类的实现。
使用内部类经常是用在Listener这样的。而Listener中的监听函数被调用时,大部分情况,new内部类所在的函数早就执行完了,此时外部类局部变量(保存在栈中)早就因为函数的退出而失效了。
而加上final后,编译器是这样处理内部类的。如果这个外部局部变量是常量,则在内部类代码中直接用这个常量。如果是类的实例,则编译器将产生一个内部类的构造参数,将这个final变量传到内部类里,这样即使外部局部变量无效了,还可以使用。
例如:
01 | import java.util.*; |
02 |
03 | public class Test{ |
04 | public void run(){ |
05 | final Lista= new
|
06 | class B{ |
07 | public int c; |
08 | public void run(){ |
09 | Listb; |
10 | b=a; |
11 | System.out.println(b); |
12 | } |
13 | }; |
14 |
15 | Bb= new B(); |
16 |
17 | b.c= 0 ; |
18 | } |
19 | } |
Compiledfrom"Test.java"classTest$1Bextendsjava.lang.Object{
publicintc;privatefinaljava.util.Listval$a;privatefinalTestthis$0;Test$1B(Test,java.util.List);publicvoidrun();}而下面是使用常量的例子:
01 | public class Test{ |
02 | public void run(){ |
03 | final int a= 100 ; |
04 | class B{ |
05 | public int c; |
06 | public void run(){ |
07 | int b; |
08 | b=a; |
09 | System.out.println(b); |
10 | } |
11 | }; |
12 |
13 | Bb= new B(); |
14 |
15 | b.c= 0 ; |
16 | } |
17 | } |
Compiledfrom"Test.java"classTest$1Bextendsjava.lang.Object{
publicintc;privatefinalTestthis$0;Test$1B(Test);Code:
0:aload_0
1:invokespecial#1;//Methodjava/lang/Object."<init>":()V
4:aload_0
5:aload_1
6:putfield#2;//Fieldthis$0:LTest;9:return
publicvoidrun();Code:
0:bipush100
2:istore_1
3:getstatic#3;//Fieldjava/lang/System.out:Ljava/io/PrintStream;6:iload_1
7:invokevirtual#4;//Methodjava/io/PrintStream.println:(I)V
10:return
}另外引用别人的一段话:
内部类并不是直接调用方法传进来的参数,而是内部类将传进来的参数通过自己的构造器备份到了自己的内部,自己内部的方法调用的实际是自己的属性而不是外部类方法的参数。
这样理解就很容易得出为什么要用final了,因为两者从外表看起来是同一个东西,实际上却不是这样,如果内部类改掉了这些参数的值也不可能影响到原参数,然而这样却失去了参数的一致性,因为从编程人员的角度来看他们是同一个东西。
如果我们不强制使用final的话,那么编程人员在程序设计的时候在内部类中改掉参数的值,但是外部调用的时候又发现值其实没有被改掉,这就让人非常的难以理解和接受,为了避免这种尴尬的问题存在,所以编译器设计人员把内部类能够使用的参数设定为必须是final来规避这种莫名其妙错误的存在。
相关文章推荐
- Java中为什么内部类对外部类局部变量有引用时,外部类局部变量一定要有final?
- Java中为什么内部类对外部类局部变量有引用时,外部类局部变量一定要有final?
- 内部类引用外部类局部变量,外部类局部变量必须是final
- Java的局部内部类以及final类型的参数和变量
- 关于java为什么在Method中局部内部类或者匿名内部类调用Method中定义的变量要用final修饰?
- Java中为什么匿名内部类和局部内部类只能访问final变量?
- 为什么返回值为数值时,返回局部变量可以,而返回值为引用时,返回局部变量不可以?
- Java 局部 final 变量与内部类引用外部变量
- 局部内部类引用外部类中的局部变量必须是final属性的!
- 局部内部类引用外部类中的局部变量必须是final属性的!
- 局部内部类引用外部类中的局部变量必须是final属性的!
- 探索,java线程中使用局部变量:局部变量必须声明为final
- 【转自JavaEye】为什么“局部内部类能且只能访问所在方法中的final类型的参数和变量”?
- 局部内部类引用外部类中的局部变量必须是final属性的!
- 内部类引用局部变量为什么要求必须是final的,而引用全局变量就不需要
- 局部内部类为什么访问方法中的局部变量时局部变量要加final
- Java的局部内部类以及final类型的参数和变量
- Java中局部内部类可以访问它所在方法中定义的final修饰的局部变量的合理解释
- Java基础-内部类-为什么局部和匿名内部类只能访问局部final变量
- 在JAVA中为什么本地变量传入内部类中要加final关键字