A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 刘忠德 于 2012-1-1 16:05 编辑

为什么内部类方法引用外部类方法参数时参数要用final修饰?为什么呢?不太理解~

评分

参与人数 1技术分 +1 收起 理由
杨强 + 1

查看全部评分

8 个回复

倒序浏览
1. 匿名内部类可以使用外部类的变量(局部或成员变来那个)
2. 匿名内部类中不同的方法可以共享这些变量
根据这两点信息我们就可以分析,可能这些变量会在匿名内部类的字段中保存着,并且在构造的时候将他们的值/引用传入内部类。这样就可以保证同时实现上述两点了。
事实上,Java就是这样设计的,并且所谓匿名类,其实并不是匿名的,只是编译器帮我们命名了而已
Java对匿名内部类的实现是通过编译器来支持的,即通过编译器帮我们产生一个匿名类的类名,将所有在匿名类中用到的局部变量和参数做为内部类的final字段,同是内部类还会引用外部类的实例

评分

参与人数 1技术分 +1 收起 理由
杨强 + 1

查看全部评分

回复 使用道具 举报
原因如下:

abstract class ABSClass{
public abstract void print();
}

public class Test2{
public static void test(final String s){//一旦参数在匿名类内部使用,则必须是final
ABSClass c=new ABSClass(){
public void print(){
System.out.println(s);
}
};
c.print();
}
public static void main(String[] args){
test("Hello World!");
}
}

JVM中每个进程都会有多个根,每个static变量,方法参数,局部变量,当然这都是指引用类型.基础类型是不能作为根的,根其实就是

一个存储地址.垃圾回收器在工作时先从根开始遍历它引用的对象并标记它们,如此递归到最末梢,所有根都遍历后,没有被标记到

的对象说明没有被引用,那么就是可以被回收的对象(有些对象有finalized方法,虽然没有引用,但JVM中有一个专门的队列引用它

们直到finalized方法被执行后才从该队列中移除成为真正没有引用的对象,可以回收,这个与本主题讨论的无关,包括代的划分等

以后再说明).这看起来很好.

但是在内部类的回调方法中,s既不可能是静态变量,也不是方法中的临时变量,也不是方法参数,它不可能作为根,在内部类中也没

有变量引用它,它的根在内部类外部的那个方法中,如果这时外面变量s重指向其它对象,则回调方法中的这个对象s就失去了引用,

可能被回收,而由于内部类回调方法大多数在其它线程中执行,可能还要在回收后还会继续访问它.这将是什么结果?

而使用final修饰符不仅会保持对象的引用不会改变,而且编译器还会持续维护这个对象在回调方法中的生命周期.所以这才是

final变量和final参数的根本意义.

评分

参与人数 1技术分 +1 收起 理由
杨强 + 1

查看全部评分

回复 使用道具 举报
本帖最后由 张绍成 于 2011-12-29 19:23 编辑

package cn.itcast.interview.test06;
public class InnerTest {
        /**
         * 方法中的内部类能不能访问方法中的局部变量,为什么?
         *
         * 答:如果局部变量用final 关键字修饰,则可以访问,否则不可以访问。
         * 首先方法中定义有一个内部类,我们只是为了控制这个类的可见性,它并不是方法的一部分我们只是代码这样写,
         * 目的是为了控制内部类的可见性,编译之后的结果,也会出现 外部类 和 内部类各自的字节码class文件,
         * 这就说明内部类是一个独立的个体,他们就是两个类而已,他们各自都有自己的成员变量和方法。
         * 并且方法的属性与内部类的生命周期不同. 方法执行完毕后,方法内的属性也会随之被销毁,
         * 然而如果让内部类在使用被销毁的属性这并不合理. 解决方法就是加上final关键字.
         * 在java中, 某个属性一旦加上final关键字, 可以看作是常量,
         * 而常量的生命周期在程序的整个执行期间
         * 都是有效的. 所以可以引用, 不会出错.
         *
         *
         * author  zsc
         * version 1.0
         * 2011-12-29
         *
         * */
  1. public static void main(String[] args) {
  2.                
  3.                 doSomething();
  4.         }
  5.         public static void doSomething(){
  6.         final int a =10;  //此处 的  a  必须用final 修饰,否则内部类将无法访问a .
  7.         class Inner{
  8.             public void seeOuter(){
  9.                 System.out.println(a);
  10.             }
  11.         }
  12.         Inner in = new Inner();
  13.         in.seeOuter();
  14.     }
  15. }
复制代码

评分

参与人数 1技术分 +1 收起 理由
杨强 + 1

查看全部评分

回复 使用道具 举报
一个方法中的内部类和方法内的属性的生命周期不同,方法执行完毕后,方法内的属性也会随之被销毁,然而此时内部类
可能还存在,这个时候内部类调用销毁的属性是不可能的,也就是说局部类的生命周期超过了方法中的属性,解决的办法
就是在变量前加上final,这样方法中的属性就会被分配在堆内存中,生命周期和方法中的内部类的生命周期一样,其内部类
就可以调用了

评分

参与人数 1技术分 +1 收起 理由
杨强 + 1

查看全部评分

回复 使用道具 举报
我学习JAVA喜欢按照自己喜欢和能理解的方式去思考为什么,final关键字英文是最终的意思,用final修饰表示是最终的,不可变更的,如果说内部内需要去引用外部类的一个变量,在内部类里又没有定义与这个变量同名的变量,但是内部内确实需要用到外部类的变量,怎么办呢?加final吧,该变量是唯一的那个,永久的那个,呵呵,就好比我们给他包装,让他成为了一个全球名人一样,全世界都知道他一样,因为这个变量变得出名了,内部类就知道这个变量的存在了吧,所以能引用到了吧  

评分

参与人数 1技术分 +1 收起 理由
杨强 + 1

查看全部评分

回复 使用道具 举报
付星 黑马帝 2011-12-30 17:45:44
7#
如果定义一个匿名内部类,并且希望它使用一个在其外部定的对象,那么编译器会要求其参数引用是final的。

1.匿名内部类肯定是局部内部类(在一个方法里面定义的内部类),因为在java中,语句必须写在方法里,而匿名内部类其实就是一条特殊的语句;
2.外部给定的对象就是所有外来的对象:外部方法的形参、局部变量、基本类型或自定义类型等。
3.内部类很微妙,它可以直接访问外部类的private field,这打破了类的封装。但是内部类又有它自身的好处,比如简洁,可见性等,于是就把它定位成“只读”,也就是final。不过这个保护也非常脆弱!
4.local inner class访问local var时,那个var必须是final的。因为可以通过enclosing class访问那个local var,也可以通过inner class访问,可能造成问题,所以就必须是final的
5.匿名内部类为什么只能用final.是变量的作用域的问题,因为匿名内部类是出现在一个方法的内部的,如果它要访问这个方法的参数或者方法中定义的变量,则这些参数和变量必须被修饰为final。因为虽然匿名内部类在方法的内部,但实际编译的时候,内部类编译成Outer.Inner,这说明内部类所处的位置和外部类中的方法处在同一个等级上,外部类中的方法中的变量或参数只是方法的局部变量,这些变量或参数的作用域只在这个方法内部有效。因为编译的时候内部类和方法在同一级别上,所以方法中的变量或参数只有为final,内部类才可以引用。

评分

参与人数 1技术分 +1 收起 理由
杨强 + 1

查看全部评分

回复 使用道具 举报
罗全涛 黑马帝 2011-12-30 17:53:46
8#
、内部类分为成员内部类、静态嵌套类、方法内部类、匿名内部类。
几种内部类的共性:
A、内部类仍然是一个独立的类,在编译之后会内部类会被编译成独立的.class文件,但是前面冠以外部类的类命和$符号。
B、内部类不能用普通的方式访问。内部类是外部类的一个成员,因此内部类可以自由地访问外部类的成员变量,无论是否是private的。
2、成员内部类:形式如下
    class Outer {
        class Inner{}
    }      
编译上述代码会产生两个文件:Outer.class和Outer$Inner.class。
3、方法内部类。
顾名思义,把类放在方法内。
    class Outer {
        public void doSomething(){
            class Inner{
                public void seeOuter(){
                }
            }   
        }
    }
A、方法内部类只能在定义该内部类的方法内实例化,不可以在此方法外对其实例化。
B、方法内部类对象不能使用该内部类所在方法的非final局部变量。
因为方法的局部变量位于栈上,只存在于该方法的生命期内。当一个方法结束,其栈结构被删除,局部变量成为历史。但是该方法结束之后,在方法内创建的内部类对象可能仍然存在于堆中!例如,如果对它的引用被传递到其他某些代码,并存储在一个成员变量内。正因为不能保证局部变量的存活期和方法内部类对象的一样长,所以内部类对象不能使用它们。
下面是完整的例子:
    class Outer {
        public void doSomething(){
            final int a =10;
            class Inner{
                public void seeOuter(){
                    System.out.println(a);
                }
            }   
            Inner in = new Inner();
            in.seeOuter();
        }
        public static void main(String[] args) {
            Outer out = new Outer();
            out.doSomething();
        }
     }
4、匿名内部类。
顾名思义,没有名字的内部类。表面上看起来它们似乎有名字,实际那不是它们的名字。
A、继承式的匿名内部类。
    class Car {
        public void drive(){
            System.out.println("Driving a car!");
        }
    }
   
    class Test{
        public static void main(String[] args) {
            Car car = new Car(){
                public void drive(){
                    System.out.println("Driving another car!");
                }
            };
            car.drive();
        }
    }
结果输出了:Driving another car! Car引用变量不是引用Car对象,而是Car匿名子类的对象。
B、接口式的匿名内部类。
    interface  Vehicle {
        public void drive();
    }
   
    class Test{
        public static void main(String[] args) {
            Vehicle v = new Vehicle(){
                public void drive(){
                    System.out.println("Driving a car!");
                }
            };
            v.drive();
        }
    }
上面的代码很怪,好像是在实例化一个接口。事实并非如此,接口式的匿名内部类是实现了一个接口的匿名类。而且只能实现一个接口。
C、参数式的匿名内部类。
class Bar{
    void doStuff(Foo f){}
}

interface Foo{
    void foo();
}

class Test{
    static void go(){
        Bar b = new Bar();
        b.doStuff(new Foo(){
            public void foo(){
                System.out.println("foofy");
            }
        });
    }
}
5、静态嵌套类。
从技术上讲,静态嵌套类不属于内部类。因为内部类与外部类共享一种特殊关系,更确切地说是对实例的共享关系。而静态嵌套类则没有上述关系。它只是位置在另一个类的内部,因此也被称为顶级嵌套类。

评分

参与人数 1技术分 +1 收起 理由
杨强 + 1

查看全部评分

回复 使用道具 举报
谢谢大家的回答哦,我明白了,呵呵~~
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马