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