Java 将数组作为线程安全参数传递时发生ActionListener错误
我正在重新编写一些代码以提高线程安全性。我尝试将数组作为参数传递,而不是将它们存储在类变量中。但当gui中的按钮需要更新/重新加载gui中面板的内容时,会抛出一个错误 看起来我需要以某种方式将这些数组传递到按钮或ActionListener,但我不知道如何做到这一点。由于问题隐藏在数千行不相关的代码中,我在下面的代码示例中重新创建了相关方面。下面代码的另一个问题是,由于某种原因,它无法使按钮可见。但除此之外,下面的代码准确地重新创建了应用程序中抛出的错误。请注意,当窗格重新加载两个数组中的数据时,代码会打印一条消息 有人能告诉我如何更改下面的代码,以便在单击按钮刷新面板内容时能够将数组作为参数传递吗 代码包含在以下三个文件中: Parent.javaJava 将数组作为线程安全参数传递时发生ActionListener错误,java,arrays,swing,thread-safety,actionlistener,Java,Arrays,Swing,Thread Safety,Actionlistener,我正在重新编写一些代码以提高线程安全性。我尝试将数组作为参数传递,而不是将它们存储在类变量中。但当gui中的按钮需要更新/重新加载gui中面板的内容时,会抛出一个错误 看起来我需要以某种方式将这些数组传递到按钮或ActionListener,但我不知道如何做到这一点。由于问题隐藏在数千行不相关的代码中,我在下面的代码示例中重新创建了相关方面。下面代码的另一个问题是,由于某种原因,它无法使按钮可见。但除此之外,下面的代码准确地重新创建了应用程序中抛出的错误。请注意,当窗格重新加载两个数组中的数据时
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Panel;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JTabbedPane;
public class Parent extends JFrame{
private static final long serialVersionUID = 1L;
JLayeredPane desktop;
JInternalFrame internalFrame;
public Parent() {
super("title goes here");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setPreferredSize(new Dimension(800, 400));
double[] myDBL = {2.3,5.4,6.5,7.8,2.4,6.4,9.0,5.3,8.1};
String[] mySTR = {"ko","lp","dk"};
Panel p = new Panel();
this.add(p, BorderLayout.SOUTH);
desktop = new JDesktopPane();
this.add(desktop, BorderLayout.CENTER);
this.pack();
this.setSize(new Dimension(800, 600));
this.setLocationRelativeTo(null);
int ifWidth = 600;
int ifHeight = 300;
internalFrame = new JInternalFrame("title", true, true, true, true);
// create jtabbed pane
JTabbedPane jtp = createTabbedPane(myDBL,mySTR);
internalFrame.add(jtp);
desktop.add(internalFrame);
internalFrame.pack();
internalFrame.setSize(new Dimension(ifWidth,ifHeight));
internalFrame.setVisible(true);
}
private JTabbedPane createTabbedPane(double[] myDBL, String[] mySTR) {
JTabbedPane jtp = new JTabbedPane();
jtp.setMinimumSize(new Dimension(600,300));
createTab(jtp, "Data",myDBL,mySTR);
return jtp;
}
private void createTab(JTabbedPane jtp, String s,double[] myDBL, String[] mySTR) {
if(s=="Data"){
PanelGUI myTimeSeriesGUI = new PanelGUI(myDBL,mySTR);
jtp.add(s,myTimeSeriesGUI);
}
else{jtp.add(s, new JLabel("TabbedPane " + s, JLabel.CENTER));}
}
public static void main(String args[]) {
Parent myParentFrame = new Parent();
myParentFrame.setVisible(true);
}
}
PanelGUI.java(此文件包含在ECLIPSE中抛出错误消息的行。)
ToolBar.java
import javax.swing.JToolBar;
import javax.swing.JButton;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import java.awt.Dimension;
public class ToolBar extends JPanel {
private static final long serialVersionUID = -2749251105543480474L;
static final private String RESET_HSCALE = "HorizontalRescale";
JButton hRescaleButton;
public ToolBar(double[] myDBL, String[] mySTR) {
//Create the toolbar.
JToolBar toolBar = new JToolBar();
addButtons(toolBar);
toolBar.setFloatable(false);
toolBar.setRollover(true);
//Lay out the main panel.
setPreferredSize(new Dimension(this.getWidth(), 40));
add(toolBar, BorderLayout.PAGE_START);
}
protected void addButtons(JToolBar toolBar) {
hRescaleButton = new JButton("Reset Horizontal Scale");
hRescaleButton.setActionCommand(RESET_HSCALE);
hRescaleButton.setToolTipText("Reset horizontal scale.");
toolBar.add(hRescaleButton);
}
}
首先分析了以下问题: 你的错误是
$> javac Parent.java
.\PanelGUI.java:38: cannot find symbol
symbol : variable myDBL
location: class PanelGUI
addComponentsToPane(this,myDBL,mySTR);//THIS IS WHERE THE ERROR IS
^
.\PanelGUI.java:38: cannot find symbol
symbol : variable mySTR
location: class PanelGUI
addComponentsToPane(this,myDBL,mySTR);//THIS IS WHERE THE ERROR IS
^
2 errors
这些变量myDBL和mySTR似乎不是对象的一部分
我注意到,您将它们作为构造函数参数传递给Toolbar,但实际上您曾经将它们保存在Toolbar类中,所以您永远无法将它们取回
我认为您需要后退一步,确定您的目标是什么,然后从那里开始。看起来您缺少一个
重新验证,这将导致添加的组件不出现。糟糕的API设计,但确实如此。有关详细信息,请参阅API文档
此外,您还应添加标准样板文件,用于从main启动Swing前端:
public static void main(final String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
runEDT();
}
});
}
很难理解您试图用这段代码实现什么。正如前面指出的,您得到的编译错误是因为您使用的变量不在范围内
就让你的动作监听器能够以你安排事情的方式看到这些变量而言,你唯一的选择就是让它们在JPanel中成为实例变量
但是,我会考虑不使用JPAND实现ActualListor。而是将hRescaleButton的侦听器创建为匿名内部类。这样可以更好地封装代码,并且不需要在处理程序中检查事件源
请参阅下面的代码
通过在addComponentsToPane方法final上声明参数,您的匿名内部类操作侦听器将能够访问它们
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class PanelGUI extends JPanel {
private static final long serialVersionUID = 1L;
public int visiblePoints;
public int newStartingPoint;
ToolBar myToolBar;
int frameWidth = 600;
int frameHeight = 300;
public PanelGUI(double[] myDBL, String[] mySTR){
newStartingPoint = 0;
visiblePoints = 5000;
addComponentsToPane(this, myDBL,mySTR);
}
public void addComponentsToPane(Container pane, final double[] myDBL, final String[] mySTR) {
pane.removeAll();
myToolBar=new ToolBar(myDBL,mySTR);
pane.add(myToolBar);
myToolBar.hRescaleButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae) {
String str = JOptionPane.showInputDialog(null, "Number of milliseconds shown in window : ", "Horizontal Rescale", 1);
if(str != null) {
int numPoints = Integer.parseInt(str);
JOptionPane.showMessageDialog(null, "You entered: "+numPoints+"ms = "+(numPoints/1000)+"sec.", "Horizontal Rescale", 1);
this.removeAll();
visiblePoints = numPoints;
frameWidth = this.getWidth();
frameHeight = this.getHeight();
setSize(frameWidth,frameHeight);
addComponentsToPane(this,myDBL,mySTR);//THIS IS WHERE THE ERROR IS
setSize(frameWidth,frameHeight);
}
else{
JOptionPane.showMessageDialog(null, "You pressed cancel button.","Horizontal Rescale", 1);
}
}});
System.out.println("pane reloaded successfully");
}
}
既然如此,为什么您认为将数组作为变量传递而不是将它们存储为实例变量可以提高线程安全性呢?不会的。如果要确保线程安全,需要确保不能同时访问和修改阵列。当您作为参数传递时,您只是复制对象引用,而实际数组不会被复制,因此代码不再是线程安全的
如果阵列传入后需要修改,则需要在访问阵列的任何位置使用公共锁同步对阵列的访问。但是,我会考虑复制数组,这样你就有了自己的副本,你知道它没有被修改。这样您就不需要任何同步。谢谢您检查我的代码。你所指出的问题正是我所寻求的帮助。我不知道如何在维护线程安全的同时执行您要求的操作,所以我要求您提供您建议的代码示例。我担心让它们成为button类的类变量会破坏我传递参数而不是使用类变量的目标。你能给我看一下解决你所说的问题的代码吗?好吧,我不知道如何在不听起来“比你更神圣”的情况下把它说出来——我知道我不是想卑鄙。。。但是我不知道你的代码应该做什么。如果不知道它应该做什么,我就不知道如何修复它。这就是为什么我说确定你的目标是什么。也许您可以在修复之前发布“线程不安全”代码,我可以看一下?目标是将两个数组传递到对象的实例中,而不是传递到类变量中。我包括了一个测试,当面板重新加载成功时,System.out.println会打印一些东西。但是代码永远不会走那么远,因为ActionPerformed方法没有访问myDBL和mySTR的权限。我精确而明确的问题是:如何为运行它的特定对象授予ActionPerformed对实例数组的访问权。@glowcoder,我想我对您下面评论的回答也回答了这个问题。如果我需要进一步澄清,请告诉我。:-)在我编译代码之前,评论就在那里。尽管对于将来的问题,我还是建议发布编译错误,因为仅仅检查代码是不明显的。在帖子中提供任何有用的信息都很重要。它显示了两件事:第一,发生了什么,总是有用的。第二,这表明你正在积极尝试解决问题,而不是在遇到路障后就放弃了。:-)我一直在做这个。我只在遇到严重路障时才发帖。我从来不会因为路障而放弃。如果我发帖,那是因为我已经尝试了很多东西,需要一双新的眼睛。你能告诉我如何特别地修改我的代码吗?我很难想出如何应用你的建议添加和删除后的代码>(如果尚未显示容器,则不需要)。(我建议不要扩展JFrame
或JPanel
,通常“更喜欢组合而不是继承”。这也适用于实现监听器,后者通常更好
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class PanelGUI extends JPanel {
private static final long serialVersionUID = 1L;
public int visiblePoints;
public int newStartingPoint;
ToolBar myToolBar;
int frameWidth = 600;
int frameHeight = 300;
public PanelGUI(double[] myDBL, String[] mySTR){
newStartingPoint = 0;
visiblePoints = 5000;
addComponentsToPane(this, myDBL,mySTR);
}
public void addComponentsToPane(Container pane, final double[] myDBL, final String[] mySTR) {
pane.removeAll();
myToolBar=new ToolBar(myDBL,mySTR);
pane.add(myToolBar);
myToolBar.hRescaleButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae) {
String str = JOptionPane.showInputDialog(null, "Number of milliseconds shown in window : ", "Horizontal Rescale", 1);
if(str != null) {
int numPoints = Integer.parseInt(str);
JOptionPane.showMessageDialog(null, "You entered: "+numPoints+"ms = "+(numPoints/1000)+"sec.", "Horizontal Rescale", 1);
this.removeAll();
visiblePoints = numPoints;
frameWidth = this.getWidth();
frameHeight = this.getHeight();
setSize(frameWidth,frameHeight);
addComponentsToPane(this,myDBL,mySTR);//THIS IS WHERE THE ERROR IS
setSize(frameWidth,frameHeight);
}
else{
JOptionPane.showMessageDialog(null, "You pressed cancel button.","Horizontal Rescale", 1);
}
}});
System.out.println("pane reloaded successfully");
}
}