精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2006-12-21
public class ShowAnonymousClass extends JFrame { Button myButton; int count; public ShowAnonymousClass() { super("Inner Class Frame"); myButton = new Button("click me"); final TextArea myTextArea = new TextArea(); //就是这句 getContentPane().add(myButton, BorderLayout.CENTER); getContentPane().add(myTextArea, BorderLayout.NORTH); myButton.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent event){ count++; myTextArea.setText("button clicked: " + count + " times"); } }); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { ShowAnonymousClass mainFrame = new ShowAnonymousClass(); mainFrame.setSize(300,300); mainFrame.setVisible(true); } } 一个很简单的例子,使用一个匿名类来处理button的事件,本来以已经习惯于匿名类只能引用上层的final变量,突然想到,当构造函数ShowAnonymousClass执行完毕之后,myTextArea这个local变量应该就失效了,当点击按钮的时候,为啥myTextArea变量对TextArea对象的应用依然有效?final 变量的具体语义究竟是什么,google一把,尽是final变量的值或引用不能改变之类的说法,查了java 语法规范也没找到,谁来说说,local变量,加了final究竟多了什么魔法?莫非类似于闭包? 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2006-12-21
>>当构造函数ShowAnonymousClass执行完毕之后,myTextArea这个local变量应该就失效了
getContentPane().add(myTextArea, BorderLayout.NORTH); myTextArea指向的对象被其他的对象(contentPane)钩住了 |
|
返回顶楼 | |
发表时间:2006-12-21
myTextArea指向的对象当然是存在的,但myTextArea作为local变量,构造函数执行完毕,本身应该实效,被垃圾回收。变量失效和变量指向的对象失效,是两回事吧。
|
|
返回顶楼 | |
发表时间:2006-12-21
如果final的local变量没有特殊的语义,innerClass似乎也没必要要求上层的local变量必须final吧,继续google
|
|
返回顶楼 | |
发表时间:2006-12-21
在一个台湾人的论坛上看到,解释的还比较合理:
http://www.javaworld.com.tw/jute/post/view?bid=29&id=66043&sty=3&age=0&tpg=1&ppg=1 大致的意思是说,innerClass对于访问的上层local变量,因为local变量的生命周期的原因,是无法直接访问的,因此,会把local变量拷贝一个副本到innerClass内作为一个field保存起来,这样一来,innerClass内部使用的那个local变量,和外层的并不是同个变量,在innerClass内部修改innerClass引用local变量的值(假设允许修改的话),并不会影响外层的local变量的值,这样就很容易引起混淆,因此,对innerClass引用的local变量加上final修饰符,限制这些变量的值不能改变,这样一来,语义就统一了,这也可以理解为什么innerClass可以直接引用外层类的变量,因为innerClass可以透过其持有的上层类实例的引用来访问其成员变量。 public class ShowAnonymousClass extends JFrame { Button myButton; private TextArea myTextArea; //把myTextArea定义为成员变量 int count; public ShowAnonymousClass() { super("Inner Class Frame"); myButton = new Button("click me"); myTextArea = new TextArea(); //去掉final getContentPane().add(myButton, BorderLayout.CENTER); getContentPane().add(myTextArea, BorderLayout.NORTH); myButton.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent event){ count++; myTextArea.setText("button clicked: " + count + " times"); } }); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { ShowAnonymousClass mainFrame = new ShowAnonymousClass(); mainFrame.setSize(300,300); mainFrame.setVisible(true); } } 如果这种说法正确的话,final 变量并没有额外的语义,而是innerClass有点类似于动态语言的闭包,从这点上看,似乎没有必要把innerClass引用的上层local变量规定为必须final,不final不是更灵活么? |
|
返回顶楼 | |
发表时间:2006-12-21
http://forum.java.sun.com/thread.jspa?threadID=736802&messageID=4232040
这个帖子说法和我上面提到的类似,有没有权威的文档来证明这种说法呢? |
|
返回顶楼 | |
发表时间:2006-12-25
怎么没人讨论,都跑去过生蛋节了?
|
|
返回顶楼 | |
发表时间:2006-12-26
今天又做了个实验,发现当local变量被innerClass引用的时候,在innerClass的生命周期没结束前,是不会被垃圾回收的:
public class ShowAnonymousClass extends JFrame { private TextArea myTextArea; public ShowAnonymousClass() { super("Inner Class Frame"); Button button1 = new Button("button1"); myTextArea = new TextArea(); getContentPane().add(button1, BorderLayout.WEST); getContentPane().add(myTextArea, BorderLayout.NORTH); final CountValue count = new CountValue(0); button1.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent event){ count.incValue(); myTextArea.setText(myTextArea.getText()+"\nbutton1 clicked: " + count.getCount() + " times"); } }); Button button2 = new Button("button2"); getContentPane().add(button2, BorderLayout.EAST); button2.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent event){ count.incValue(); myTextArea.setText(myTextArea.getText()+"\nbutton2 clicked: " + count.getCount() + " times"); } }); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { ShowAnonymousClass mainFrame = new ShowAnonymousClass(); mainFrame.setSize(300,300); mainFrame.setVisible(true); } public class CountValue{ private int count; public CountValue(int count){ this.count = count; } public int getCount(){ return this.count; } public void incValue(){ this.count ++; } } } 运行这段代码可以发现,在其中一个innerClass内修改CountValue的值,会影响另外一个innerClass引用的CountValue,很显然,这两个innerClass的CountValue引用,指向同一个对象,这和拷贝上层的local变量引用的对象到innerClass内部,显然是矛盾的,还是搞不清楚Java的local变量的作用域问题 |
|
返回顶楼 | |
发表时间:2007-01-11
balaschen 写道 运行这段代码可以发现,在其中一个innerClass内修改CountValue的值,会影响另外一个innerClass引用的CountValue,很显然,这两个innerClass的CountValue引用,指向同一个对象,这和拷贝上层的local变量引用的对象到innerClass内部,显然是矛盾的,还是搞不清楚Java的local变量的作用域问题 不矛盾, 拷贝的是到对象的引用, 而不是克隆复制对象. 这其实是编译器的特殊处理. 最简单的研究办法, 写这么一个类: public class ShowAnony { public void test(final int n) { new Runnable() { public void run() { int m = n+1; } }.run(); } } 编译以后再 javap: D:\temp>javap -c ShowAnony$1 Compiled from "ShowAnony.java" final class ShowAnony$1 extends java.lang.Object implements java.lang.Runnable{ final int val$n; final ShowAnony this$0; ShowAnony$1(ShowAnony, int); Code: 0: aload_0 1: aload_1 2: putfield #1; //Field this$0:LShowAnony; 5: aload_0 6: iload_2 7: putfield #2; //Field val$n:I 10: aload_0 11: invokespecial #3; //Method java/lang/Object."<init>":()V 14: return public void run(); Code: 0: aload_0 1: getfield #2; //Field val$n:I 4: iconst_1 5: iadd 6: istore_1 7: return } 一切都清楚了. 要求必须加 final 应该是为了语义的一致性. 不然匿名类被构造好以后, 上层变量如果被重新赋值, 但匿名类从语义上引用外层变量, 值却还是它构造时候的, 逻辑上就错误了. |
|
返回顶楼 | |
发表时间:2007-01-11
complystill 写道 balaschen 写道 运行这段代码可以发现,在其中一个innerClass内修改CountValue的值,会影响另外一个innerClass引用的CountValue,很显然,这两个innerClass的CountValue引用,指向同一个对象,这和拷贝上层的local变量引用的对象到innerClass内部,显然是矛盾的,还是搞不清楚Java的local变量的作用域问题 不矛盾, 拷贝的是到对象的引用, 而不是克隆复制对象. 这其实是编译器的特殊处理. 最简单的研究办法, 写这么一个类: public class ShowAnony { public void test(final int n) { new Runnable() { public void run() { int m = n+1; } }.run(); } } 编译以后再 javap: D:\temp>javap -c ShowAnony$1 Compiled from "ShowAnony.java" final class ShowAnony$1 extends java.lang.Object implements java.lang.Runnable{ final int val$n; final ShowAnony this$0; ShowAnony$1(ShowAnony, int); Code: 0: aload_0 1: aload_1 2: putfield #1; //Field this$0:LShowAnony; 5: aload_0 6: iload_2 7: putfield #2; //Field val$n:I 10: aload_0 11: invokespecial #3; //Method java/lang/Object."<init>":()V 14: return public void run(); Code: 0: aload_0 1: getfield #2; //Field val$n:I 4: iconst_1 5: iadd 6: istore_1 7: return } 一切都清楚了. 要求必须加 final 应该是为了语义的一致性. 不然匿名类被构造好以后, 上层变量如果被重新赋值, 但匿名类从语义上引用外层变量, 值却还是它构造时候的, 逻辑上就错误了. 拷贝的是引用(也就是地址)。 如果不是定义成final,然后在其他地方对这个变量进行赋值的话, 这个变量的地址就变了。 那么这个匿名类里面的地址就有可能是空的。应该是这样理解吧。 PS: 看不懂javap之后的东西。 |
|
返回顶楼 | |