关于java:内部JButton的actionperformed最终变量是否需要?

inside JButton's actionperformed final variables required?

本问题已经有最佳答案,请猛点这里访问。

所以我有一个JList,我试图在JButtonactionPerformed方法中使用它,它要求我制作JListfinal,为什么下面是一个代码片段

1
2
3
4
5
6
public SomeClass() {    
  btnNewButton.addActionListener(new ActionListener() {
       public void actionPerformed(ActionEvent e) {
         list.clearSelection();            
    }});
}

实际上,我在最终决定时没有问题,我只是不知道为什么我需要。


要回答您的问题,您需要了解一些基本知识,比如JVM如何工作。当编译包含内部类的类时,生成的字节代码实际上并不将内部类作为类内的类来实现。

错误原因:局部变量是从内部类访问的,需要声明为final

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JMenu;
import javax.swing.JPanel;

public class foo extends JPanel
{  
  public foo()
  {
    final JMenu edit = new JMenu();
    edit.getItem(0).addMouseListener(new MouseAdapter(){
    @Override
        public void mouseClicked(MouseEvent e)
        {
            if (e.getClickCount() == 1) {
                edit.getItem(0).setEnabled(true);
            }
        }
    });
  }
}

编译这个程序时,将创建两个文件:foo.class和foo$1.class。所以现在你的问题来了,因为Second类,即foo$1.class不知道Variableedictakbd存在于First类,即foo.class中。

那么如何解决这个问题呢?JVM所做的是,它要求开发人员将外部类的变量声明为最终变量。

现在这样做了,现在JVM悄悄地将一个名为val$edit的隐藏变量放在第二个编译类文件中,这里是从javap获得的输出。

输出用于foo.class

1
2
3
4
5
C:\Mine\JAVA\J2SE\folder>javap foo.class
Compiled from"foo.java"
public class foo extends javax.swing.JPanel {
  public foo();
}

现在,由于edit是构造函数本地的,因此输出如上所述。

1
2
3
4
5
6
7
8
C:\Mine\JAVA\J2SE\folder>javap foo$1.class
Compiled from"foo.java"
class foo$1 extends java.awt.event.MouseAdapter {
  final javax.swing.JMenu val$edit;
  final foo this$0;
  foo$1(foo, javax.swing.JMenu);
  public void mouseClicked(java.awt.event.MouseEvent);
}

Variableval$edit被分配了相同的值,这个值从现在开始被分配给edit,编译器知道这个值不能被改变,因为它已经被声明为最终值,因此这次它是有效的。

现在,如果我把editKBDVariableLocal改为Instance呢?现在,类的对象知道这个变量edicitakbd的所有信息,如果它被更改的话。因此,改变上述程序,我们也得到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JMenu;
import javax.swing.JPanel;

public class foo extends JPanel
{  
    JMenu edit = new JMenu();

    public foo()
    {  
        edit.getItem(0).addMouseListener(new MouseAdapter(){
        @Override
            public void mouseClicked(MouseEvent e)
            {
            if (e.getClickCount() == 1) {
                    edit.getItem(0).setEnabled(true);
                }
            }
        });
    }
}

在这种情况下,我们不应该将其声明和定义为final,因为在这种情况下,由于Variable是整个类的局部,VariableObject Reference一起发送给内部类,即this

1
2
3
4
5
6
C:\Mine\JAVA\J2SE\folder>javap foo.class
Compiled from"foo.java"
public class foo extends javax.swing.JPanel {
  javax.swing.JMenu edit;
  public foo();
}

以下是在这种情况下发送Variable的方式,即0美元:

1
2
3
4
5
6
7
C:\Mine\JAVA\J2SE\folder>javap foo$1.class
Compiled from"foo.java"
class foo$1 extends java.awt.event.MouseAdapter {
  final foo this$0;
  foo$1(foo);
  public void mouseClicked(java.awt.event.MouseEvent);
}

据我看来,这就是解释,这种情况是如何运作的。刚才我在互联网上发现了这个关于当地内部阶级可接近性的神秘解释,也许这能帮助你更好地理解这种情况:—)


通过将list变量设为final变量,它们不再是真正的变量ref,而是常量。然后,编译器可以用常量的值(当然,在编译时)替换匿名类中使用list,这样就不再有访问不存在变量的问题了。


您正在创建一个匿名类,并且list变量的作用域太有限,因此您无法访问它。除非你把它变成final或是SomeClass的字段。

有关更多信息,请参阅本文