java launcher to support running a program supplied as a single
file of Java source code.
Single-file programs -- where the whole program fits in a single source file --
are common in the early stages of learning Java, and when writing small utility
programs. In this context, it is pure ceremony to have to compile the program
before running it. In addition, a single source file may compile to multiple
class files, which adds packaging overhead to the simple goal of "run this
program". It is desirable to be able to run the program directly from source
The Java launcher currently operates in three modes: launching a class, a JAR, or the main program of a module. A new, fourth mode is added: launching a class declared in a source file.
Source-file mode is determined by considering two items on the command line.
- The first item on the command line that is neither an option nor part of an option. (In other words, the item that until now has been the class name.)
- An optional
If the "class name" identifies an existing file with the
source-file mode will be selected, with that file to be compiled and run.
If the file does not have the
.java extension, the
--source option may
be used to force source-file mode. This is for cases such as when the
source file is a "script" to be executed and the name of the source file
does not follow the normal naming conventions for Java source files. (See
"Shebang" files below.)
In source-file mode, the effect is as though the source-file is compiled into
memory, and the first class found in the source file is executed.
For example, if a file called
HelloWorld.java contains a class called
hello.World, then the command
will be equivalent to
javac -d <memory> HelloWorld.java java -cp <memory> hello.World
Any arguments placed after the name of the source file in the original command
line will be passed to the compiled class when it is executed. For example,
if a file called
Factorial.java contains a class called
Factorial to calculate
the factorials of its arguments, then the command
java Factorial.java 3 4 5
will be equivalent to
javac -d <memory> Factorial.java java -cp <memory> Factorial 3 4 5
Java command-line argument files (@-files) may be used in the standard way
in source-file mode. Long lists of arguments for either the VM or the program
being invoked may be placed in files which are specified on the command-line
by prefixing the filename with an
Note that there is a potential minor ambiguity when using a simple command-line
java HelloWorld.java. Previously,
HelloWorld.java would have been
interpreted as a class called
java in a package called
HelloWorld, but which
will now be resolved in favor of a file called
HelloWorld.java if such a file
exists. Given that such a class name and such a package name both violate the
nearly-universally-followed naming conventions, and given the unlikeliness of
such a class being on the class path and a like-named file being in the current
directory, this seems an acceptable compromise.
Source-file mode requires the presence of the
jdk.compiler module. When
source-file mode for a file
Foo.java is requested, the launcher will behave as
if the command line were translated to:
java [ VM args ] -m jdk.compiler/jdk.somepackage.SourceLauncher Foo.java [ program args ]
SourceLauncher class will programmatically invoke the compiler, compile
the source to an in-memory representation, create a class loader to load
compiled classes from that in-memory representation, and invoke the standard
main(String) method of the first top-level class found in the source file, in a
class loading environment that includes the class path and module path specified
on the command line, as well as the newly compiled classes. This environment
will be in an unnamed module. For both the compilation and subsequent execution,
it will be as though
--add-modules=ALL-DEFAULT is in effect. Any arguments
appearing after the name of the file on the command line will be passed to the
main(String) method in the obvious way. For "shebang" files, this includes
options specified in the first line of the file, and any arguments provided on the
command line when the file is executed.
When the compiler is invoked, it will have access to any relevant VM args, such as those to define the class path, module path, and the module graph, and will use those arguments to configure the compilation environment. No provision is made to tunnel any additional options to the compiler. Implicit compilation of additional source files is not supported.
In source-file mode, the compiler does not enforce the optional restriction defined at the end of
that a type in a named package should exist in a file whose name is
composed from the type name followed by the
If the class that is invoked throws an exception, that exception will be passed back to the launcher for handling in the normal way. However, the initial stackframes leading up to the execution of the class will be removed from the stacktrace of the exception. The intent is that the handling of the exception will be similar to the handling if the class had been executed directly by the launcher itself.
Single-file programs are also common when the task at hand needs a small utility program.
In this context, it is desirable to be able to run a Java program directly from
source using the "#!" mechanism
on Unix-derived systems, such as MacOS and Linux. This is a mechanism provided
by the operating system which allows script or source code to be placed in any
conveniently named executable file whose first line begins with
#! and which
specifies the name of a program to "execute" the contents of the file.
A "shebang" file to invoke the Java launcher using source-file mode will typically begin with something like:
To allow for such files in source-file mode, if the file begins with
contents of the first line up to but not including the first newline are ignored
by the launcher, while the rest of the file is passed to the compiler,
The content of the file that appears after the first line must consist of a valid
CompilationUnit as defined by the edition of the Java Language Specification
that is appropriate to the version of the platform being used to run the program.
As such, no changes to the JLS are required in support of this feature.
If the source file contains errors, appropriate error messages will be written
to the standard error stream, and the launcher will exit with a non-zero exit code.
When the file begins with
#!, the newline at the end of the first line is
preserved so that the line numbers in any error messages remain unchanged.
In a "shebang" file, the first two bytes must be
0x23 0x21, the two-character
ASCII encoding of
#!. All subsequent bytes are read with the default platform
character encoding that is in effect.
A first line beginning
#! is only required when it is desired to execute the
file with the operating system's "shebang" mechanism. There is no need for any
special first line when the Java launcher is used explicitly to run the code in
a source file, as in the
Factorial.java examples, given
The status quo has worked for 20+ years; we could continue to do so.
We could create a source launcher, but call it something else besides
jrun. Given the number of execution modes the launcher already
has, this would likely be perceived as a gratuitous difference.
We could delegate the task of "one-off runs" to the
jshell tool. While this
may at first seem obvious, this was an explicit non-goal in the design of
jshell tool was designed to be an interactive shell, and many
design decisions were made in favor of providing a better interactive
experience. Burdening it with the additional constraints of being the batch
runner would detract from the interactive experience.