在javaFX中Application是一个抽象类。在这个抽象类中,有一些抽象方法在扩展抽象应用程序类的主类中被重写,并且在抽象类应用程序中也有静态 launch() 方法。 launch() 方法从主类中的 main 方法调用。现在 launch() 方法如何调用这些抽象方法,并且对于这些调用,执行主类中的重写方法?请帮助我了解这个程序实际上是有效的。 我知道非静态方法不能从静态方法调用,并且不可能创建抽象类的实例。抽象方法不能是静态方法。
我不想创建主类的对象。因为launch()方法不知道主类的名字是什么。
给我写一段java代码,其中这个过程可以很好地说明。像这样
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();
}
我想获得输出:你好
Application#launch(String...)
方法定义为:
启动独立应用程序。此方法通常从
方法调用。 [...]。这相当于main
,其中launch(TheClass.class, args)
是调用 launch 的方法的直接封闭类。TheClass
因此,它确实通过获取“调用者类”来知道主类的名称。然后,它通过反射创建应用程序类的实例,并根据需要调用 init()
和
start(Stage)
方法。
Application
子类的实例化以及
init()
、
start(Stage)
和
stop()
的调用都是JavaFX生命周期的一部分(由
Application
记录)。您不实例化应用程序类,也不调用这些方法。它由 JavaFX 处理。 请注意,您不应该拥有自己的
Application
课程。将类命名为与您正在使用的框架中的类相同的名称不仅会令人困惑,而且对您没有任何帮助。如果您想要正常的 JavaFX 生命周期,您需要延长
javafx.application.Application
。JavaFX 是如何做到的?
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);
}
然后其他一些代码可以做到:
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));
}
}
无需将课程传递给launch
。如上所述,这与 JavaFX 实际所做的相比大大简化,并且它使用了一些不同的 API。例如:
java.lang.StackWalker
(在Java 9中添加)。 JavaFX 目前(截至 21-ea)仍然使用通过
Thread.currentThread().getStackTrace()
手动遍历堆栈跟踪的代码。
JavaFX 应用程序线程 不是像我上面的“应用程序事件线程”那样手动创建的。相反,FX 线程由 JavaFX 框架管理,真正的启动代码使用 Platform#runLater(Runnable)
的内部版本。
init()
方法的“JavaFX-Launcher Thread”所做的类似操作。
start(String[])
,然后死亡(一旦
start
返回)。这不是JavaFX 应用程序线程 的工作方式,它本质上是在循环中运行。
Preloader
。
Stage
实例,并将其传递给
start(Stage)
方法;我通过传递给定的命令行参数数组来“模拟”这一点。但请注意,在真实的 JavaFX 应用程序中,如果将命令行参数传递给
launch
,那么这些参数将封装在
Parameters
对象中(并进行基本解析)。
遵循源代码。