Как JButton вызывает метод actionPerformed?

Я пытался понять, как именно работает объект JButton.

Я создал приведенный ниже объект SampleFrame, который расширяет JFrame.

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;

public class SampleFrame extends JFrame implements ActionListener{
    
    SampleFrame() {
        JButton button = new JButton("Hello");
        add(button);
        button.addActionListener(this);
    }
    @Override
    public void actionPerformed(ActionEvent arg0) {
        System.out.println("Hello");
    }

    public static void main(String[] args) {
        new SampleFrame().setVisible(true);
    }
}

Я понимаю, что add(button) добавляет объект JButton в JFrame, а button.AddActionLister(this) добавляет объект SampleFrame к набору прослушивателей этого события, поэтому его метод actionPerformed вызывается каждый раз при нажатии кнопки. Чего я не понимаю, так это как именно называется этот метод actionPerformed? Это какой-то поток, где это вызывается? Извините, если это звучит слишком наивно, я не слишком знаком с Java (и потоками).

Не уверен, имеет ли это какое-либо отношение к этому, но когда я закрываю фрейм, код все равно отображается в консоли как работающий. Это указывает мне на то, что параллельно выполняется своего рода цикл while.

Вы можете значительно облегчить себе задачу, исключив из этого сценария все потоки. Отдельных тем нет. Фактически, вся деятельность Swing происходит в одном и только одном потоке, поэтому вам вообще не нужно об этом беспокоиться. Собственный код JButton (или, по крайней мере, некоторые внутренние классы, которые использует JButton) будет явно просматривать список добавленных прослушивателей ActionListener всякий раз, когда пользователь активирует кнопку, и вызывать метод actionPerformed каждого такого прослушивателя.

VGR 10.06.2024 00:43
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
2
52
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Давайте перепишем ваш пример кода, чтобы он соответствовал обычному подходу приложения Swing, как показано в классе HelloWorldSwing из руководства Swing, бесплатно предоставляемого Oracle.

public class OneButtonSwingApp
{
    /**
     * Create & show GUI.  For thread safety, invoke from the event-dispatching thread.
     */
    private static void createAndShowGUI ( )
    {
        // Create and set up the window.
        JFrame frame = new JFrame( "One Button Example" );
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );

        // Widgets
        JLabel timeLabel = new JLabel( "Now: " + Instant.now( ) );
        JButton tellTimeButton = new JButton( "Tell time" );

        // Behavior
        tellTimeButton.addActionListener( ( ActionEvent actionEvent ) -> { timeLabel.setText( "Now: " + Instant.now( ) ); } );

        // Arrange
        frame.setLayout( new FlowLayout( ) );
        frame.getContentPane( ).add( timeLabel );
        frame.getContentPane( ).add( tellTimeButton );

        // Display the window.
        frame.pack( );
        frame.setVisible( true );
    }

    public static void main ( String[] args )
    {
        // Schedule a job for the event-dispatching thread:
        // creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(
                new Runnable( )
                {
                    public void run ( )
                    {
                        createAndShowGUI( );
                    }
                } );
    }
}

Каждое Java-приложение запускается в потоке для выполнения метода main.

Приложения Swing имеют дополнительный поток. Эта тема посвящена запуску графического интерфейса. См. учебник Oracle: Поток отправки событий.

В нашем коде здесь, в нашем начальном потоке приложения, мы вызываем SwingUtilities.invokeLater, чтобы запланировать позднее выполнение некоторого кода в этом потоке диспетчеризации событий. В рамках этого вызова мы передаем объект анонимного класса, реализующего интерфейс Runnable. Реализация метода run необходима для выполнения контракта Runnable.

В современной Java мы бы упростили этот main метод с помощью ссылки на метод .

    public static void main ( String[] args )
    {
        // Schedule a job for the event-dispatching thread:
        // creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater( OneButtonSwingApp :: createAndShowGUI );
    }

В Swing, как и в любой другой среде графического интерфейса, которую я когда-либо видел, вы никогда не получаете доступ к элементам графического интерфейса и не манипулируете ими из другого потока. Всегда используйте поток диспетчеризации событий для запуска любого кода для доступа к элементам графического интерфейса и манипулирования ими.

добавление объекта SampleFrame в набор прослушивателей этого события, чтобы его метод actionPerformed вызывался каждый раз при нажатии кнопки

Да, наш вызов tellTimeButton.addActionListener передает лямбду (код, который будет выполнен позже) коллекции слушателей. Вызов этого метода означает, что мы сообщаем кнопке «Вот код, который будет запускаться всякий раз, когда происходит событие действия».

В своем коде вы передали сам экземпляр SampleFrame в качестве слушателя. Хотя создание прослушивателя объекта JFrame может работать, я бы не рекомендовал этого делать. Держите свои объекты четко очерченными, каждый из которых имеет единственную причину существования. Создайте отдельный объект с поведением, которое вы хотите запускать в ответ на нажатие кнопки.

Позже, во время выполнения, платформа Swing отслеживает действия пользователя. Когда пользователь нажимает эту кнопку, кнопка проходит через свою коллекцию прослушивателей, чтобы выполнить приведенный ранее код. Код выполняется в потоке диспетчеризации событий. Поэтому крайне важно, чтобы ваш код прослушивателя был коротким и понятным, поскольку пользовательский интерфейс зависает/перестает отвечать на запросы во время выполнения этого кода. Если вам предстоит выполнить длительную задачу, перейдите к другому потоку с помощью SwingWorker.

Чего я не понимаю, так это того, как именно вызывается этот метод actionPerformed?

Swing — это 👉🏽событийно-управляемая структура. Это означает, что структура отвечает за мониторинг событий, поступающих от пользователя или из других источников. Поток управления в вашем приложении не является главным. Вы заранее настраиваете прослушиватели, а затем код прослушивателя выполняется по мере возникновения конкретных событий.


Вам действительно стоит изучить Учебное пособие Oracle по Swing, чтобы понять эти ключевые понятия.

Последующие действия. Могу ли я использовать Swing, но удалить «поток отправки событий» и написать свой собственный код, который будет выполнять все задачи, связанные с графическим интерфейсом, в том же потоке, что и основной метод? Раньше я использовал pygame (на Python) для создания графических интерфейсов, и мне не приходилось беспокоиться о других потоках, работающих параллельно. Спрашиваю об этом, потому что мне бы хотелось максимально избежать многопоточности. Я также открыт для изучения других альтернатив Swing.

Umang Lunia 12.06.2024 21:46

@UmangLunia Нет, вы не можете/не должны пытаться победить поток отправки событий в Swing. Чтобы запустить код в потоке отправки событий, просто прикрепите прослушиватели к виджетам Swing. Например, см. вызов tellTimeButton.addActionListener в примере выше. Переданная туда лямбда действительно будет выполнена в потоке отправки событий. Остерегайтесь: как обсуждалось в ответе, пользовательский интерфейс зависает (не отвечает) пользователю, пока такой код выполняется в потоке отправки событий. Только для долго выполняющегося кода вам понадобится еще один поток. Опять же, поработайте с учебником Oracle.

Basil Bourque 13.06.2024 00:02

Другие вопросы по теме