如何从代码中关闭 JavaSwing 应用程序

从代码中终止 Swing 应用程序的正确方法是什么,有哪些缺陷?

我曾试图在计时器触发后自动关闭应用程序。但是仅仅在 JFrame上调用 dispose()并没有起到作用——窗口消失了,但是应用程序没有终止。但是,当使用关闭按钮关闭窗口时,应用程序将终止。我该怎么办?

256484 次浏览

Try:

System.exit(0);

Crude, but effective.

I guess a EXIT_ON_CLOSE

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

before System.exit(0) is better since you can write a Window Listener to make some cleaning operations before actually leaving the app.

That window listener allows you to defined:

public void windowClosing(WindowEvent e) {
displayMessage("WindowListener method called: windowClosing.");
//A pause so user can see the message before
//the window actually closes.
ActionListener task = new ActionListener() {
boolean alreadyDisposed = false;
public void actionPerformed(ActionEvent e) {
if (frame.isDisplayable()) {
alreadyDisposed = true;
frame.dispose();
}
}
};
Timer timer = new Timer(500, task); //fire every half second
timer.setInitialDelay(2000);        //first delay 2 seconds
timer.setRepeats(false);
timer.start();
}


public void windowClosed(WindowEvent e) {
//This will only be seen on standard output.
displayMessage("WindowListener method called: windowClosed.");
}

If I understand you correctly you want to close the application even if the user did not click on the close button. You will need to register WindowEvents maybe with addWindowListener() or enableEvents() whichever suits your needs better.

You can then invoke the event with a call to processWindowEvent(). Here is a sample code that will create a JFrame, wait 5 seconds and close the JFrame without user interaction.

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


public class ClosingFrame extends JFrame implements WindowListener{


public ClosingFrame(){
super("A Frame");
setSize(400, 400);
//in case the user closes the window
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
//enables Window Events on this Component
this.addWindowListener(this);


//start a timer
Thread t = new Timer();
t.start();
}


public void windowOpened(WindowEvent e){}
public void windowClosing(WindowEvent e){}


//the event that we are interested in
public void windowClosed(WindowEvent e){
System.exit(0);
}


public void windowIconified(WindowEvent e){}
public void windowDeiconified(WindowEvent e){}
public void windowActivated(WindowEvent e){}
public void windowDeactivated(WindowEvent e){}


//a simple timer
class Timer extends Thread{
int time = 10;
public void run(){
while(time-- > 0){
System.out.println("Still Waiting:" + time);
try{
sleep(500);
}catch(InterruptedException e){}
}
System.out.println("About to close");
//close the frame
ClosingFrame.this.processWindowEvent(
new WindowEvent(
ClosingFrame.this, WindowEvent.WINDOW_CLOSED));
}
}


//instantiate the Frame
public static void main(String args[]){
new ClosingFrame();
}


}

As you can see, the processWindowEvent() method causes the WindowClosed event to be fired where you have an oportunity to do some clean up code if you require before closing the application.

Your JFrame default close action can be set to "DISPOSE_ON_CLOSE" instead of EXIT_ON_CLOSE (why people keep using EXIT_ON_CLOSE is beyond me).

If you have any undisposed windows or non-daemon threads, your application will not terminate. This should be considered a error (and solving it with System.exit is a very bad idea).

The most common culprits are java.util.Timer and a custom Thread you've created. Both should be set to daemon or must be explicitly killed.

If you want to check for all active frames, you can use Frame.getFrames(). If all Windows/Frames are disposed of, then use a debugger to check for any non-daemon threads that are still running.

I think, the idea is here the WindowListener - you can add any code there that you'd like to run before the thing shuts down

The following program includes code that will terminate a program lacking extraneous threads without explicitly calling System.exit(). In order to apply this example to applications using threads/listeners/timers/etc, one need only insert cleanup code requesting (and, if applicable, awaiting) their termination before the WindowEvent is manually initiated within actionPerformed().

For those who wish to copy/paste code capable of running exactly as shown, a slightly-ugly but otherwise irrelevant main method is included at the end.

public class CloseExample extends JFrame implements ActionListener {


private JButton turnOffButton;


private void addStuff() {
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
turnOffButton = new JButton("Exit");
turnOffButton.addActionListener(this);
this.add(turnOffButton);
}


public void actionPerformed(ActionEvent quitEvent) {
/* Iterate through and close all timers, threads, etc here */
this.processWindowEvent(
new WindowEvent(
this, WindowEvent.WINDOW_CLOSING));
}


public CloseExample() {
super("Close Me!");
addStuff();
}


public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
CloseExample cTW = new CloseExample();
cTW.setSize(200, 100);
cTW.setLocation(300,300);
cTW.setVisible(true);
}
});
}
}

May be the safe way is something like:

    private JButton btnExit;
...
btnExit = new JButton("Quit");
btnExit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e){
Container frame = btnExit.getParent();
do
frame = frame.getParent();
while (!(frame instanceof JFrame));
((JFrame) frame).dispose();
}
});

In response to other comments, DISPOSE_ON_CLOSE does not seem to properly exit the application - it only destroys the window, but the application will continue running. If you want to terminate the application use EXIT_ON_CLOSE.

Take a look at the Oracle Documentation.

Starting from JDK 1.4 an Application terminates if:

  • There are no displayable AWT or Swing components.
  • There are no native events in the native event queue.
  • There are no AWT events in java EventQueues.

Cornercases:

The document states that some packages create displayable components without releasing them.A program which calls Toolkit.getDefaultToolkit() won't terminate. is among others given as an example.

Also other Processes can keep AWT alive when they, for what ever reason, are sending events into the native event queue.

Also I noticed that on some Systems it takes a coupple of seconds before the Application actually terminates.