java 中为什么引用外部类的变量和方法都要是final内型 的

如题所述

Thinking In Java里面的说法(唯一正确的说法): 如果定义一个匿名内部类,并且希望它使用一个在其外部定的对象,那么编译器会要求其参数引用是final 的。以下是分析过程:

首先看代码


    public class Tester {     
        public static void main(String[] args) {     
            A a = new A();     
            C c = new C();     
            c.shoutc(a.shout(5));     
        }     
    }     
    ////////////////////////////////////////////////////////     
    class A {     
        public void shouta() {     
            System.out.println("Hello A");     
        }     
        
        public A shout(final int arg) {     
            class B extends A {     
                public void shouta() {     
                    System.out.println("Hello B" + arg);     
                }     
            }     
            return new B();     
        }     
    }     
    ////////////////////////////////////////////////////////     
    class C {     
        void shoutc(A a) {     
            a.shouta();     
        }     
    }

第5行c.shoutc(a.shout(5)),在a.shout(5)得到返回值后,a的shout()方法栈被清空了,即arg不存在了,而c.shoutc()却又调用了a.shouta()去执行System.out.println("Hello B" + arg)。

再来看Java虚拟机是怎么实现这个诡异的访问的:有人认为这种访问之所以能完成,是因为arg是final的,由于变量的生命周期,事实是这样的吗?方法栈都不存在了,变量即使存在,怎么可能还被访问到?试想下:一个方法能访问另一个方法的定义的final局部变量吗(不通过返回值)?

研究一下这个诡异的访问执行的原理,用反射探测一下局部内部类 。编译器会探测局部内部类中是否有直接使用外部定义变量的情况,如果有访问就会定义一个同类型的变量,然后在构造方法中用外部变量给自己定义的变量赋值,而后局部内部类所使用的变量都是自己定义的变量,所以就可以访问了。见下:

    class   A$1$B   
    {   
    A$1$B(A,   int);   
     
    private   final   int   var$arg;   
    private   final   A   this$0;   
    }



A$1$B类型的对象会使用自定义的var$arg变量,而不是shout()方法中的final int arg变量,当然就可以访问了。

那么为什么外部变量要是final的呢?即使外部变量不是final,编译器也可以如此处理:自己定义一个同类型的变量,然后在构造方法中赋值就行了。原因就是为了让我们能够挺合逻辑的直接使用外部变量,而且看起来是在始终使用 外部的arg变量(而不是赋值以后的自己的字段)。

考虑出现这种情况:在局部内部类中使用外部变量arg,如果编译器允许arg不是final的,那么就可以对这个变量作变值操作(例如arg++),根据前面的分析,变值操作改变的是var$arg,而外部的变量arg并没有变,仍然是5(var$arg才是6)。因此为了避免这样如此不合逻辑的事情发生:你用了外部变量,又改变了变量的值,但那个变量却没有变化,自然的arg就被强行规定必须是final所修饰的,以确保让两个值永远一样,或所指向的对象永远一样(后者可能更重要)。

还有一点需要注意的是内部类与方法不是同时执行的,比如实现ActionListener,只有当事件发生的时候才会执行,而这时方法已经结束了。

温馨提示:答案为网友推荐,仅供参考
第1个回答  2012-10-18
您好,提问者:
这里我给你举个例子,请看下面:
final类不能被继承,没有子类,final类中的方法默认是final的。
final方法不能被子类的方法覆盖,但可以被继承。
final成员变量表示常量,只能被赋值一次,赋值后值不再改变。
final不能用于修饰构造方法。

问题重点:final的变量是不可修改的,比如你用final声明数据库账号、密码,你不可能改变它,所有定义成final之后才被引用,增加了安全性,要想修改只能去final声明那里修改,在程序调用中是不可能修改的!本回答被网友采纳
第2个回答  2012-10-18
其实不一定非得那样,不考虑其他的你可以在一个类中定义成static 然后,在另一个类中通过:
类名.属性 或 类名.方法调用。不过这样做的话属性值就可能被调用的类改变,而且很耗内存,一个合格的程序员在选择方法时通常会考虑内存、性能、效率等。

现在final最主要的作用就是:方便、高效。
第一、防止其他类继承 ,修改它的属性
第二、高效。编译器在遇到调用final方法时候会转入内嵌机制,大大提高执行效率。追问

我是在一个内部类中引用外部那个类的变量,如果引用的不是final型变量就会报错

追答

其实fanal定义的变量就是一个常量!写成final在内部类中用一定不会错!
你什么地方错了发下代码过来吧。

追问

就是我写了个内部类,引用了外部内的变量,然后提示说只能引用final类型的变量,程序是没问题了,我想知道这是为什么!系统都说必须引用final类型的外部变量我知道肯定有其道理。

追答

把代码复制过来看下就知道了,这样讲不是很清楚,不知道具体的问题

第3个回答  2012-10-22
lz 表达不清楚,居然说是引用外部类的变量,是不是说的内部类? 也就是内部类使用外部类的变量,必须是final???
第4个回答  2012-10-18
楼上的都是在装内行 答非所问 回答的都是final的意义; 这个问题当时我也只是强记的,希望楼主知道后也能告知声哦
相似回答