java swing——正确创建并启动一个swing应用

java swing开发中,JFrame作为启动窗口,使用时不建议直接new一个JFrame,推荐把JFrame放到一个事件指派线程中启动,也就是通过SwingUtilities.invokeLater方式

package com.zhufeng.demo;

import javax.swing.*;
import java.awt.*;

/**
 * @ClassName: ShowJFrame
 * @Description TODO
 * @author 月夜烛峰
 * @date 2022/7/11 17:09
 */
public class ShowJFrame {

    JPanel colorPanel = new JPanel();

    public void createAndShowGUI(String title) {
        //窗口名称
        JFrame frame = new JFrame("月夜烛峰-" + title);
        //窗口带有关闭按钮
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //指定窗口大小
        frame.setSize(new Dimension(400, 300));
        //窗口居中展示
        frame.setLocationRelativeTo(null);
        //窗口设置为可见
        frame.setVisible(true);
        colorPanel.setName(title);
        colorPanel.setBackground(Color.BLACK);
        frame.add(colorPanel);
    }

    public static void main(String[] args) {

        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                ShowJFrame frame = new ShowJFrame();
                frame.createAndShowGUI("正常启动");
                frame.colorPanel.setBackground(Color.GREEN);
            }
        });

    }
}

运行Main方法,可以正常启动

通过SwingUtilities.invokeLater启动GUI:

一方面是另起一个线程,可以异步执行,出现异常时,也不至于主线程宕掉;

另一方面通过事件派发线程,在GUI有需要更新的事件时,可以保证线程安全的执行。

为了能够更直观的感受两者的不同,可以通过普通线程和事件指派线程分别演示下启动GUI时可能导致的一些问题。

新建一普通线程 SwingThreadDemo.java,代码如下:

package com.zhufeng.demo;

import java.awt.*;

/**
 * @ClassName: SwingThreadDemo
 * @Description TODO
 * @author 月夜烛峰
 * @date 2022/7/11 17:36
 */
public class SwingThreadDemo extends Thread {

    @Override
    public void run() {
        ShowJFrame frame = new ShowJFrame();
        frame.createAndShowGUI("普通线程启动");
        frame.colorPanel.setBackground(Color.BLUE);
        System.out.println("普通线程启动: " + Thread.currentThread());
    }
}

普通线程中,把背景色设置为蓝色和指派线程进行区分,并打印线程信息

改造指派线程代码

package com.zhufeng.demo;

import javax.swing.*;
import java.awt.*;

/**
 * @ClassName: ShowJFrame
 * @Description TODO
 * @author 月夜烛峰
 * @date 2022/7/11 17:09
 */
public class ShowJFrame {

    JPanel colorPanel = new JPanel();

    public void createAndShowGUI(String title) {
        //窗口名称
        JFrame frame = new JFrame("月夜烛峰-" + title);
        //窗口带有关闭按钮
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //指定窗口大小
        frame.setSize(new Dimension(400, 300));
        //窗口居中展示
        frame.setLocationRelativeTo(null);
        //窗口设置为可见
        frame.setVisible(true);
        colorPanel.setName(title);
        colorPanel.setBackground(Color.BLACK);
        frame.add(colorPanel);
    }

    public static void main(String[] args) {
        SwingThreadDemo thread = new SwingThreadDemo();
        thread.start();
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                ShowJFrame frame = new ShowJFrame();
                frame.createAndShowGUI("正常启动");
                frame.colorPanel.setBackground(Color.GREEN);
                System.out.println("指派线程启动: " + Thread.currentThread());
            }
        });

    }
}

在main方法中,同时运行普通线程和指派线程:

 通过控制台可以看到,线程打印的线程信息有差异,SwingUtilities.invokeLater运行的线程为AWT-EventQueue(关于代码实现可以自行查看源码,比较简单)。

注意: 

虽然看上去通过两种方式均可以正常运行,但是这里有个陷阱,看上去同时启动,但因为我们调用了指派线程,指派线程的运行又影响swing的运行事件,从而间接普通线程的运行(比如背景色渲染),所以看上去没有运行问题。

如果我们只运行普通线程:

 这里的背景色变成了默认背景颜色,既不是绿色也不是蓝色。

代码中,把指派线程运行逻辑注释掉:

package com.zhufeng.demo;

import javax.swing.*;
import java.awt.*;

/**
 * @ClassName: ShowJFrame
 * @Description TODO
 * @author 月夜烛峰
 * @date 2022/7/11 17:09
 */
public class ShowJFrame {

    JPanel colorPanel = new JPanel();

    public void createAndShowGUI(String title) {
        //窗口名称
        JFrame frame = new JFrame("月夜烛峰-" + title);
        //窗口带有关闭按钮
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //指定窗口大小
        frame.setSize(new Dimension(400, 300));
        //窗口居中展示
        frame.setLocationRelativeTo(null);
        //窗口设置为可见
        frame.setVisible(true);
        colorPanel.setName(title);
        colorPanel.setBackground(Color.BLACK);
        frame.add(colorPanel);
    }

    public static void main(String[] args) {
        SwingThreadDemo thread = new SwingThreadDemo();
        thread.start();
        /**
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                ShowJFrame frame = new ShowJFrame();
                frame.createAndShowGUI("正常启动");
                frame.colorPanel.setBackground(Color.GREEN);
                System.out.println("指派线程启动: " + Thread.currentThread());
            }
        });
        */
    }
}

通过简单的对比,关于正常运行一个swing应用,确实也是需要注意。


版权声明:本文为superli0426原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。