RFR: 8329420: Java 22 (and 23) launcher calls default constructor although main() is static
Jan Lahoda
jlahoda at openjdk.org
Fri Apr 12 10:21:02 UTC 2024
Consider code like:
public class MainClass {
public MainClass() {
System.out.println("Constructor called!");
}
public static void main() {
System.out.println("main called!");
}
}
and compile and run it, with preview enabled, like:
$ javac /tmp/MainClass.java
$ java --enable-preview -classpath /tmp MainClass
Constructor called!
main called!
That is wrong, as the `main` method is static, and there is no need to create a new instance of the class.
The reason is that as launcher attempts to invoke the main method, it goes in the following order: 1) static-main-with-args; 2) instance-main-with-args; 3) static-main-without-args; 4) instance-main-without-args. But, for the instance variants, the code first creates a new instance of the given class, and only then attempts to lookup the `main` method, and will pass to the next option when the `main` method lookup fails. So, when invoking static-main-without-args, the current class instance may be created for instance-main-with-args, which will then fail due to the missing `main(String[])` method.
The proposed solution to this problem is to simply first do a lookup for the `main` method (skipping to the next variant when the given main method does not exist, without instantiating the current class).
There is also a relatively closely related problem: what happens when the constructor throws an exception?
public class MainClass {
public MainClass() {
if (true) throw new RuntimeException();
}
public void main() {
System.out.println("main called!");
}
}
when compiled an run, this produces no output whatsoever:
$ javac /tmp/MainClass.java
$ java --enable-preview -classpath /tmp MainClass
$
This is because any exceptions thrown from the constructor are effectively ignored, and the launcher will continue with the next variant. This seems wrong - the exception should be printed for the user, like:
$ java --enable-preview -classpath /tmp/ MainClass
Exception in thread "main" java.lang.RuntimeException
at MainClass.<init>(MainClass.java:3)
This patch proposes to do that by not consuming the exceptions thrown from the constructor, and stop the propagation to the next variant.
-------------
Commit messages:
- Updating year.
- 8329420: Java 22 (and 23) launcher calls default constructor although main() is static
Changes: https://git.openjdk.org/jdk/pull/18753/files
Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=18753&range=00
Issue: https://bugs.openjdk.org/browse/JDK-8329420
Stats: 150 lines in 2 files changed: 130 ins; 4 del; 16 mod
Patch: https://git.openjdk.org/jdk/pull/18753.diff
Fetch: git fetch https://git.openjdk.org/jdk.git pull/18753/head:pull/18753
PR: https://git.openjdk.org/jdk/pull/18753
More information about the core-libs-dev
mailing list