swing与线程问题
Posted chenllingmeng
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了swing与线程问题相关的知识,希望对你有一定的参考价值。
程序如下:
import javax.swing.*;
class SwingDemo {
private static void createAndShowGUI() {
// Create a new JFrame container.
JFrame jfrm = new JFrame("A Simple Swing Application");
// Give the frame an initial size.
jfrm.setSize(275, 100);
// Terminate the program when the user closes the application. jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Create a text-based label.
JLabel jlab = new JLabel(" Swing defines the modern Java GUI.");
// Add the label to the content pane.
jfrm.getContentPane().add(jlab);
// Display the frame.
jfrm.setVisible(true);
}
public static void main(String args[]) {
// Create the frame on the event dispatching thread.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
程序的第一句话是import语句:
import javax.swing.*;
这是因为javax.swing包中包含了Swing中的组件和容器。
接下来,程序定义了SwingDemo类以及它的默认构造函数,其中创建了图形程序中的全部内容。构造函数中首条语句:
JFrame jfrm = new JFrame("A Simple Swing Application");
创建了JFrame对象jfrm。JFrame是顶层容器,带有最小化、最大化、关闭按钮。并将字符串传入到JFrame的构造函数中,作为窗口的标题。
jfrm.setSize(275, 100);
设置了窗口的宽和高的值,单位是像素。
jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
默认情况下,关闭顶层窗口并不会关闭应用程序,而只是把窗口从屏幕上移除。但是大多数情况不需要这种行为,关闭窗口意味着关闭整个应用程序。如果需要这样,可以调用setDefaultCloseOperation()函数。
这个方法的一般形式是:
void setDefaultCloseOperation(int what);
传入参数what决定了当窗口关闭时会发生什么。除了JFrame.EXIT_ON_CLOSE,其它的包括:
JFrame.DISPOSE_ON_CLOSE
JFrame.HIDE_ON_CLOSE
JFrame.DO_NOTHING_ON_CLOSE
从它们的名字里我们就可以猜出含义。这些常量定义在WindowConstants中,它是在javax.swing中定义的接口(interface),JFrame实现了它。
下面的代码创建了JLable组件,
JLabel jlab = new JLabel(" Swing defines the modern Java GUI.");
JLabel组件的作用是显示字符串或者图片,它只能输入信息,没有办法接受输入。
jfrm.getContentPane().add(jlab);
所有的顶层容器都包含一个叫content pane(内容面板)的容器(中间层容器,默认类型为JPanel)来存储组件。为了将组件加到frame中,需要将它加到frame的content pane中。getContentPane()函数的原型是
Container JFrame.getContentPane()
其中Container是AWT中定义的容器类,JComponent是它的直接子类,而JPanel是JComponent的直接子类。这里实际上返回的是一个JPanel,即content pane内容面板是一个JPanel对象。
Container的add()方法有很多(重载)版本,这里使用的是
Component Container.add(Component comp)
默认情况下,JFrame的content pane的布局管理器的类型是BorderLayout。BorderLayout布局管理器将容器分为东、南、西、北、中心五个方位,上面版本的add()函数将组件放入到中心位置。当组件加入到容器的中心位置,它的大小将调整到充满中心。其它版本的add()函数可以将组件加入到容器中的其它位置。
在JDK 5之后上面的代码可以写成
jfrm.add(jlab);
需要清楚jlab不是直接增加到JFrame中,而是JFrame包含的content pane中,上面的写法只是为了方便而提供的。而且,这样写就不需要接触AWT中的容器类型Container了。但是目前还有很多代码是老的写法的代码。为了了解背后的原理,我们还是以老的方式来增加控件。
jfrm.setVisible(true);
将jfrm显示出来,默认情况下JFrame是不显示的。
最后在main()函数中,需要这样写
public static void main(String args[]) {
// Create the frame on the event dispatching thread.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
那为什么不能简单的在main()函数中直接调用createAndShowGUI()呢?
public static void main(String args[]) {
// Create the frame on the main thread
// which is not event dispatching thread.
createAndShowGUI();
}
如果这样写,可能发现程序也能够运行,但是基于线程安全的考虑,不能这样写。
首先,线程方面的知识告诉我们从不同的线程中访问同一个数据会造成数据出现问题。对于窗口程序的界面元素它们实际上也是数据(即Swing窗口元素对象,只是绘图代码将它们绘制出来了),为了避免在多个线程中对Swing窗口对象进行操作也会出现各种各样的问题(比如创建、删除、更新界面元素以及进行事件处理),需要在整个程序的某一个线程中处理界面操作。这个线程就是事件派发线程。这个线程是系统自动创建的。而main()函数的所在的线程是另外的线程,在包括main()函数在内的其它线程中访问Swing对象是不好的,可能会出现问题。因此,提供了invokeLater等方法,它们会将代码转到事件派发线程中来执行。
SwingUtilities.invokeLater()函数的样子是
static void invokeLater(Runnable obj),
而Runnable是定义在java.lang包中的接口,代码
new Runnable() {
public void run() {
createAndShowGUI();
}
}
以上是关于swing与线程问题的主要内容,如果未能解决你的问题,请参考以下文章
Java开发中的线程安全选择与SwingUtilities类的invokeLater()或invokeAndWait()