0

in javaFX Application is an abstract class. In this abstract class there have some abstract methods which are overridden in main class which extends abstract Application class and also have static launch() method in abstract class Application. The launch() method calls from the main method in main class. Now how is it possible launch() method calls these abstract methods and for these calls overridden methods in main class are executes? please help me to understand this procedure actually works. I know that non-static method can't invoked from static method and it is not possible to create instance of an abstract class. abstract method can't be a static method.

I don't want to create object of main class. Because the launch() method don't know what is the name of main class.

Write me a java code where this procedure illustrate well. like this

public class main extends Application{
    public static void main(String[] args)
        launch();                       // launch() was a static method in Application class
    }
    @override
    public void init(){                 // init() was an abstract method in Application class
        System.out.println("hello");
    }
}

public abstract class Application {
    public static void launch(){
        init();
    }
    public abstract void init();
}

I want to get output : hello

Nahid
  • 13
  • 3
  • This is nothing special about JFX. Have you seen the [abstract classes tutorial](https://docs.oracle.com/javase/tutorial/java/IandI/abstract.html)? The abstract class says what methods need to be defined, and the compiler can make sure you do define them, so it's safe to call them. – Robert Jul 30 '23 at 17:24
  • 4
    @Robert Actually there are special things about the JavaFX application [launch process](https://github.com/openjdk/jfx/blob/master/modules/javafx.graphics/src/main/java/javafx/application/Application.java#L274) IMO. It works in a highly unusual way, as it uses a combination of stack trace call back trace and reflection to work out call stack history and invoke historical methods. That doesn't negate your comments around abstract classes though. – jewelsea Jul 30 '23 at 19:10
  • Additionally, in earlier versions of JavaFX, launch was integrated with JVM startup similar to but different from main method processing, so you could execute an application that [launched the VM and did not even define a main class](https://stackoverflow.com/questions/47139115/running-javafx-app-without-main-method). That was also kind of a special IMO. I don't think the technology exists in recent JavaFX distributions, but some of the strange handling of how launch works may be a legacy of that design. – jewelsea Jul 30 '23 at 19:11
  • 1
    Make sure you understand the [application lifecycle](https://openjfx.io/javadoc/20/javafx.graphics/javafx/application/Application.html). Don't define your own class named `Application`, that is not even a JavaFX `Application`. That is super confusing. – jewelsea Jul 30 '23 at 19:16
  • 4
    @jewelsea The "special handling" that allows omitting the main method in JavaFX applications actually [is still part of the current OpenJDK versions](https://github.com/openjdk/jdk/blob/ad34be1f329edc8e7155983835cc70d733c014b8/src/java.base/share/classes/sun/launcher/LauncherHelper.java#L1070). I once submitted an issue for a separate problem, though it was related to the JavaFX startup process, and [Kevin had argued removing this "special handling" would be too disruptive.](https://github.com/javafxports/openjdk-jfx/issues/570#issuecomment-524546199) Looks like they never revisited it. – Slaw Jul 30 '23 at 22:46
  • "Note that the `start` method is abstract and must be overridden." – trashgod Jul 30 '23 at 23:21
  • Related: [*JavaFX runtime main method*](https://stackoverflow.com/q/52434404/642706) – Basil Bourque Jul 31 '23 at 20:01
  • Related: [*JavaFX Application class - difference between default constructor and init method*](https://stackoverflow.com/q/34786120/642706) – Basil Bourque Aug 08 '23 at 04:58

2 Answers2

6

The Application#launch(String...) method is defined to:

Launch a standalone application. This method is typically called from the main method. [...]. This is equivalent to launch(TheClass.class, args) where TheClass is the immediately enclosing class of the method that called launch.

So, it does know the name of the main class by getting the "caller class". It then creates an instance of the application class via reflection and invokes the init() and start(Stage) methods as appropriate.

The instantiation of the Application implementation1 and the invoking of init(), start(Stage), and stop(), are all part of the JavaFX life-cycle (documented by Application). You don't instantiate the application class, nor do you call those methods. It's handled by JavaFX.

Note you should not have your own Application class. Not only is it confusing to name a class the same as a class from the framework you're using, but it does nothing for you. You need to extend javafx.application.Application if you want the normal JavaFX life-cycle.


1. It's not the Application class itself that's instantiated, as it's abstract. However, you must provide a concrete subclass (i.e., implementation) as part of your project, and it's that class that's instantiated. Of course, from the point of view of JavaFX, it only knows it has an instance of Application. But that is basic polymorphism at work.


How Does JavaFX Do It?

Here's some non-JavaFX code demonstrating the process. Note it's vastly simplified and does things slightly differently from the actual JavaFX implementation.

package com.example;

import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;

public abstract class Application {

  private static final AtomicBoolean LAUNCHED = new AtomicBoolean();
  private static Throwable error;

  public static void launch(Class<? extends Application> appClass, String... args) {
    Objects.requireNonNull(appClass);
    Objects.requireNonNull(args);
    if (!LAUNCHED.compareAndSet(false, true)) {
      throw new IllegalStateException("already launched");
    }

    CountDownLatch startLatch = new CountDownLatch(1);

    Thread eventThread = new Thread(() -> {
      try {
        Application instance = appClass.getConstructor().newInstance();
        instance.start(args);
      } catch (Throwable error) {
        Application.error = error;
      } finally {
        startLatch.countDown();
      }
    });
    eventThread.setName("Application Event Thread");
    eventThread.start();

    try {
      startLatch.await();
    } catch (InterruptedException ex) {
      throw new RuntimeException(ex);
    }

    if (error != null) {
      throw new RuntimeException("Exception in Application start method", error);
    }
  }

  @SuppressWarnings("unchecked")
  public static void launch(String... args) {
    StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
    Class<?> callerClass = walker.getCallerClass();
    if (!Application.class.isAssignableFrom(callerClass)) {
      throw new IllegalStateException("caller class is not a subclass of com.example.Application");
    }
    launch((Class<? extends Application>) callerClass, args);
  }

  public abstract void start(String[] args);
}

Then some other code can do:

package com.example;

import java.util.Arrays;

public class Main extends Application {

  public static void main(String[] args) {
    launch(args);
  }

  @Override
  public void start(String[] args) {
    System.out.println("Started! args = " + Arrays.toString(args));
  }
}

Without having to pass the class to launch.

As noted, this is vastly simplified from what JavaFX actually does, and it uses some different APIs. For instance:

  • I used java.lang.StackWalker (added in Java 9). JavaFX currently (as of 21-ea) still uses code that manually walks the stack trace via Thread.currentThread().getStackTrace().

  • The JavaFX Application Thread is not created manually like I did for the "Application Event Thread" above. Instead, the FX thread is managed by the JavaFX framework, and the real launch code makes use of the internal version of Platform#runLater(Runnable).

    • However, the real JavaFX code does do something similar to what I did for the "JavaFX-Launcher Thread" used to invoke the init() method.

    • My code has the "Application Event Thread" instantiate the class, call start(String[]), and then die (once start returns). That's not how the JavaFX Application Thread works, which essentially runs in a loop.

  • The real JavaFX code has to deal with potential Preloaders.

  • The real JavaFX code creates a Stage instance that it passes to the start(Stage) method; I "simulate" that by passing the given command line arguments array. Though note in a real JavaFX application, if you pass the command line arguments to launch, then those arguments are encapsulated in a Parameters object (and rudimentarily parsed).

  • In the real JavaFX code, the thread that calls launch is blocked until the framework exits.

If you're truly curious about the entire process, then I recommend you follow the source code.

Slaw
  • 37,820
  • 8
  • 53
  • 80
5

[Caveat: I am not an expert on JavaFX. I may well be wrong on some points.]

tl;dr

The abstract javafx.application.Application class is not being instantiated. As you correctly note, instantiating an abstract class would violate the Java language specification.

The JavaFX framework uses the magic of reflection to instantiate an object of a concrete class, your subclass of Application.

The non-static methods init, start, and stop defined by Application are being implemented by your subclass of Application. It is those implementations you write on your concrete subclass that are being executed by the JavaFX framework.

So concrete methods on an object of a concrete class, no abstract involved.

Lifecycle

Let's review the lifecycle of a modern JavaFX/OpenJFX app.

main

When any kind of Java app launches, the JVM executes the designated main method in a thread dedicated to that app.

launch

In a JavaFX app, that main method should be written to call either of the two overloaded static methods javafx.application.Application.launch. The class containing this main method must either (a) extend from javafx.application.Application, or (b) the call to launch must pass the Class object of your class that does extend from Application.

The launch method is only called once per life of your app. And the launch method does not return until the end of the app’s life.

Something like this:

public class TimeApplication extends Application
{
    public static void main ( String[] args )
    {
        javafx.application.Application.launch ();
    }
}

init

Immediately after loading and constructing the Application class via your subclass, the Application#init method is automatically called. This call happens on a background thread, as seen in my example code below. This background thread is not the original app thread, and is not the JavaFX Application Thread discussed below.

The default implementation of that method does nothing. You may choose to provide an implementation that overrides this method. That override is your hook to perform initialization crucial to your app before any of the GUI has been established.

You may want to use the init override to establish necessary resources, and verify the operating environment. For example, you may want to verify that expected external services are indeed available such as user authenticators, web services, message queues, and database servers. Your init method might spin off threads to prepare information later needed by your user.

But you cannot construct a Stage or Scene in init as the JavaFX display framework has not yet been established. You may construct other JavaFX objects. You set the Scene & Stage and their contents in the start method running on a separate thread.

Simple example:

public class TimeApplication extends Application
{
    @Override
    public void init ( ) throws Exception  // On original app thread.
    {
        if ( ! userAuthServiceIsAvailable ( ) )
        {
            // … handle failure … 
        }
    }

    public static void main ( String[] args )  // On original app thread.
    {
        Application.launch ( );
    }
}

start

After the init method (either your override or the default no-op) completes its execution, then Application#start is automatically called. The JavaFX framework automatically constructs and passes a Stage object representing the initial window of your app.

To quote the Javadoc:

The main entry point for all JavaFX applications. The start method is called after the init method has returned, and after the system is ready for the application to begin running.

The Application#start method is the place to establish the initial GUI for your app. You construct a Scene object, and assign it to the passed Stage object.

This method executes on a new thread established for JavaFX GUI event handling. This thread is known in the Javadoc as JavaFX Application Thread.

Note that start is marked abstract, which means the Application class does not contain an implementation of this method. This means your subclass of Application must contain an implementation for this method — if omitted your app would not compile. Note the contrast to init and stop methods which are not abstract and have an implementation in Application (an implementation that happens to do nothing, but an implementation nevertheless). So writing an override for init & stop is optional for us, but writing a start method is required.

stop

The counterpart to Application#start is Application#stop. The method is automatically called by the JavaFX framework when the app is going to quit/exit.

The default implementation does nothing. You may choose to override this method as a hook for you to close resources and perform other clean-up chores appropriate to the app ending.

This method executes on the JavaFX Application Thread.

Example code

package work.basil.example;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.time.Instant;

public class TimeApplication extends Application
{
    @Override
    public void init ( ) throws Exception
    {
        System.out.println ( "The init method is running on thread " + Thread.currentThread ( ).threadId ( ) + " “" + Thread.currentThread ( ).getName ( ) + "”" + " at " + Instant.now ( ) );
        if ( ! this.userAuthServiceIsAvailable ( ) )
        { /* … handle failure … */ }
    }

    @Override
    public void start ( Stage stage )
    {
        System.out.println ( "The start method is running on thread " + Thread.currentThread ( ).threadId ( ) + " “" + Thread.currentThread ( ).getName ( ) + "”" + " at " + Instant.now ( ) );

        // Scene
        Button button = new Button ( );
        button.setText ( "Tell time" );
        button.setOnAction ( ( event ) -> System.out.println ( Instant.now ( ) ) );

        VBox vbox = new VBox ( button );
        Scene scene = new Scene ( vbox );

        // Stage
        stage.setTitle ( "Time keeps on slipping, slipping, slipping" );
        stage.setHeight ( 300 );
        stage.setWidth ( 500 );

        stage.setScene ( scene );
        stage.show ( );
    }

    @Override
    public void stop ( ) throws Exception
    {
        System.out.println ( "The stop method is running on thread " + Thread.currentThread ( ).threadId ( ) + " “" + Thread.currentThread ( ).getName ( ) + "”" + " at " + Instant.now ( ) );
    }

    private boolean userAuthServiceIsAvailable ( )
    {
        return true;
    }

    public static void main ( String[] args )
    {
        System.out.println ( "The main method is beginning on thread " + Thread.currentThread ( ).threadId ( ) + " “" + Thread.currentThread ( ).getName ( ) + "”" + " at " + Instant.now ( ) );
        Application.launch ( );
        System.out.println ( "The main method is ending on thread " + Thread.currentThread ( ).threadId ( ) + " “" + Thread.currentThread ( ).getName ( ) + "”" + " at " + Instant.now ( ) );
    }
}

Be aware that output to System.out does not necessarily appear in chronological order when called across threads. Include timestamps, and study them, to verify sequence of actions.

Example output:

The main method is beginning on thread 1 “main” at 2023-07-31T19:57:37.255140Z
The init method is running on thread 27 “JavaFX-Launcher” at 2023-07-31T19:57:37.262522Z
The start method is running on thread 24 “JavaFX Application Thread” at 2023-07-31T19:57:37.268091Z
2023-07-31T19:57:39.206604Z
2023-07-31T19:57:39.391998Z
2023-07-31T19:57:39.524548Z
The stop method is running on thread 24 “JavaFX Application Thread” at 2023-07-31T19:57:42.402692Z
The main method is ending on thread 1 “main” at 2023-07-31T19:57:42.404950Z

Summary of lifecycle

Here is my table of lifecycle activities. Note that init and start run at the same time, not sequentially, running on separate threads.

Sequence main
thread
JavaFX-Launcher
thread
JavaFX Application Thread
thread
1 main method
2 Your call to Application.launch
(blocks)
3 Construction of your subclass of Application
via reflection
by the JavaFX framework
4 Either no-op or your override of Application#init Your implementation of Application#start
(you establish Scene & Stage)
5 init may continue running indefinitely • User events
• Animation timelines
6 • User closes last window
• Or, Platform.exit called on this or any other thread
7 Either no-op or your override of Application#stop
8 Application.launch returns

Sequence step # 3 is when the JavaFX framework uses reflection to instantiate an object of your subclass of Application.

Your questions

in javaFX Application is an abstract class.

Yes. Being abstract means you are intended to write a subclass of Application. Thus we have TimeApplication extends Application in code above.

some abstract methods which are overridden in main class which extends abstract Application class and also have static launch() method

Yes, both launch methods are static. So we call them with syntax like this: Application.launch(). No reference to an object.

We usually make the static call Application.launch() from the static method main. So we have a static call within another static call — no objects involved.

The launch() method calls from the main method in main class.

Be aware that the main method may or may not be housed within the class inheriting from Application.

The JavaFX framework examines the class containing the Application.launch call. If that class extends the abstract class Application, then you can use the launch method with only a single argument, args. If you have an arrangement where the call to Application.launch takes place in some class that is not a subclass of Application, then you must use the other launch method where you pass a Class object that represents the class that is a subclass of Application.

What may be confusing you is the lack of a reference to an object of your subclass of Application. In our example code above, at no point do we see an instantiation like this:

TimeApplication timeApp = new TimeApplication() ;

The reason we see no such instantiation is because of some “magic” in JavaFX. The JavaFX framework is using reflection to instantiate our subclass on our behalf.

The construction of our subclass happens behind the scenes, with no reference in our sight. At no point do we get back a reference to the object of our subclass. See discussion on the Question Getting application instance in javafx. If you really want a reference to the object of your subclass of Application, see the workaround in this Answer.

Now how is it possible launch() method calls these abstract methods and for these calls overridden methods in main class are executes?

The static call to Application.launch results in the concrete subclass (TimeApplication in our example) inheriting from the abstract Application class. So we have an object, a TimeApplication object, in memory, but we do not see any reference to it. That object has the override methods of init, start, and stop. So we have a concrete object, not abstract, with regular overrides methods being executed at various points in time on various threads. The Java rules you mention are being followed properly, as described in these two bullets:

  • I know that non-static method can't invoked from static method

We are not invoking non-static methods from a static context. The non-static init, start, and stop methods are being executed on an object of our concrete subclass TimeApplication that was automatically instantiated for us, not by us. The calls to those methods init, start, and stop are also being made for us, not by us.

  • and it is not possible to create instance of an abstract class.

You are correct — No instance was made of the abstract class Application. The JavaFX used the magic of reflection to create, behind the curtains, an instance of our subclass of Application. Via reflection, JavaFX created an instance of TimeApplication in our example above. The framework instantiated an object of a concrete subclass extending an abstract class.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • 1
    If you print out the [name](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Thread.html#getName())s of the threads as well as the IDs, I think, in this case, you get some additional useful information. – jewelsea Jul 31 '23 at 07:22
  • A notable part of this, is that if the main class specified at startup (i.e., passed to the `java` tool) is a subclass of `Application`, the main class is actually _changed_ to an internal class: `LauncherHelper$FXHelper`. If the `Application` subclass _also_ has a proper `main` method, it's actually JavaFX that invokes it via reflection (and it then waits for a call to `launch` to continue the process of launching the application). And if I'm not mistaken, the JavaFX toolkit is fully initialized before the `Application` subclass is instantiated (possibly even before `main` is invoked). – Slaw Jul 31 '23 at 09:44
  • 1
    Check out [one of my other answers](https://stackoverflow.com/a/52447692/6395627). It was written after debugging the code for JavaFX 10/11, so some things might have changed by now, but I believe it's still generally correct. Most of that information, however, is of course implementation details. – Slaw Jul 31 '23 at 09:45
  • @jewelsea I altered code to include thread name. Thanks for the tip. – Basil Bourque Jul 31 '23 at 20:00