При создании окна создаётся
Event Dispatch Thread, внутри которого крутится бесконечный цикл, на каждой итерации достающий событие из очереди и запускающий обработчик для него. В обработчике бесполезно использовать циклы для изменения интерфейса, так как все изменения просто встанут в очередь и будут выполнены только на одной из следующих итераций цикла событий. И тем более нельзя останавливать
Поток Обработки Событий (что вы делаете вызовом Thread.sleep(1000)), это заморозит всё приложение. Поэтому необходимо использовать предлагаемые библиотекой механизмы запуска фоновых задач и взаимодействия с ними. Например такие, как
SwingUtilities.invokeLater(),
Timer и
SwingWorker.
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JButton;
import javax.swing.Timer;
public class Countdown {
private final JLabel label = new JLabel("...");
private final JButton button = new JButton("Click me");
private final Timer timer;
private int count = 3;
public Countdown() {
timer = new Timer(1000, e -> {
if (count > 0) {
label.setText(String.valueOf(count--));
} else {
((Timer) (e.getSource())).stop();
count = 3;
button.setEnabled(true);
}
});
timer.setInitialDelay(0);
button.addActionListener(e -> {
timer.start();
button.setEnabled(false);
});
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(label, BorderLayout.PAGE_START);
frame.add(button, BorderLayout.PAGE_END);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
new Countdown();
}
}