1. Introduction
Java 11 introduced a new feature of launching single-file source-code programs. A single-file program is one where the program fits in a single source file. Before Java 11, to run even a single line program we had to first compile the code and then run the program. For example, to run a HelloWorld.java
program, first we have to compile the program.
javac HelloWorld.java
This will generate .class
file.
Then execute the program using java
launcher.
java HelloWorld.java
In this tutorial, we’ll discuss this new feature which allows developers to run Java source code directly using java
launcher, without the need to explicitly compile it beforehand. This works by the java launcher automatically invoking the compiler and storing the compiled code in-memory. This feature is very helpful when you want to get started with Java or explore new features of Java without going through the hard work of compiling and then executing the code. However, there are some limitations of this feature. In this tutorial, we’ll discuss how to use this feature and its limitations.
2. Executing Your First Single-File Source-Code Program
To demonstrate, we’ll create a hello world
program. To execute a single-file source-code program, the first class defined in the source file must contain public static void main(String[])
.
public class HelloWorld { public static void main(String[] args) { System.out.println("Hello World!"); } }
From the command line, HelloWorld
can be launched with java
launcher.
java HelloWorld.java
Notice how we passed the Java source code file name and not the Java class to the java
command. We’ll get compilation errors if the source file contains errors.
Important: The name of the Source file name and the name of the class containing main
method need not be same.
3. Passing in Arguments
Arguments can also be passed in like with a normally compiled class.
public class HelloJava { public static void main(String[] args) { System.out.println("Hello " + args[0]); } }
Here is an example of executing this code with arguments.
> java HelloJava.java World!
Also, we can pass options likes –class-path before the file name:
> java --class-path=/some-path HelloJava.java World!
We’ll get an error if there is a class on the application classpath with the same name as the class we are executing.
4. Multiple Classes in Same File
If needed for encapsulation purposes, multiple classes can be defined within the same source file.
public class MultipleClassesInSameFileExample { public static void main(String[] args) { System.out.println(Message.generateMessage()); System.out.println(AnotherMessage.generateAnotherMessage()); } } class Message { static String generateMessage() { return "Some message!"; } } class AnotherMessage { static String generateAnotherMessage() { return "Another message"; } }
> java MultipleClassesInSameFile.java
Output
Here is one message
Here is another message
Note: A class that is part of the core JDK does not need to be added to the classpath to be executed.
5. Using Source-File Mode to Launch Single-File Source-Code Programs
To run a class declared in a source file, run the java
launcher in source-file mode. The source-file mode is enabled if one of the following two conditions are true:
- The first item on the command line followed by the JVM options is a file name with the
.java
extension. - The
--source
option is specified.
If the file does not have the .java
extension, the --source
option must be used to tell the java
command to use the source-file mode. The --source
option is used when the source file is a “script” meant for execution, and its name does not conform to the usual Java source file naming conventions. In the source-file mode, class is compiled in memory and the class in the source file is executed.
Here are some important points about the source-file mode:
- In source-file mode annotation processing is disabled.
- The source file is compiled in the context of an unnamed module.
- The source file should include one or more top-level classes, with the first class the one to be executed. The first top-level class must contain a declaration of the standard
public static void main(String[])
method. - If there are errors in the source file, the appropriate error messages are output to the standard error stream, and the launcher will exit with a non-zero exit code.
- The compiled classes are loaded by a custom class loader that delegates to the application class loader. As a result, classes on the application class path cannot reference any classes declared in the source file.
- An error occurs if there is a class on the application class path with the same name as the class to be executed.
6. Implementation
The mode for handling source files mandates that the jdk.compiler
module be available. If one opts for source-file mode for a file named HelloWorld.java
, the launcher behaves as if the command line were translated to:
java [VM args] \ -m jdk.compiler/<source-launcher-implementation-class> \ HelloWorld.java [program args]
The source-launcher implementation class programmatically invokes the compiler, which compiles the source to an in-memory representation. The source-launcher implementation class then creates a class loader to load compiled classes from that in-memory representation, and invokes the standard main(String[])
method of the first top-level class found in the source file.
7. Shebang Files
In Unix-derived systems, like macOS and Linux, scripts are popular way of running some utility programs. For example, a shell script typically starts with:
#!/bin/sh
This mechanism provided by the operating system which allows a single-file program (such as a 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. Such files are called “shebang files”.
It was desirable to execute the Java programs with this mechanism. A shebang file to invoke the Java launcher using source-file mode must begin with something like:
#!/path/to/java --source version
For example, if the file containing our “Hello World” program is HelloWorld
, after an initial line of #!/path/to/java --source 10
, and then mark the file as executable. Then, if the file is in the current directory, we could execute it with:
$ ./HelloWorld
Or, if the file is in a directory in the user’s PATH, we could execute it with:
$ HelloWorld
Any arguments for the command are passed to the main method of the class that is executed. For example:
$ HelloWorld SomeMessage
The --source
option must be used in shebang files in the following situations:
- The name of the shebang file does not follow the standard naming conventions for Java source files.
- If you want to include additional VM options in the first line of the shebang file, ensure that the
--source
option is specified immediately after the name of the executable. - It is desired to specify the version of the Java language used for the source code in the file.
$ java -Dtrace=true --source 10 HelloWorld 3
If the file is not a Java source file (not having extension .java) and if the first line begins with #!
, then the contents of that line up to but not including the first newline are ignored when determining the source code to be passed to the compiler.
In a shebang file, the first two bytes must be 0x23 0x21, the two-character ASCII encoding of #!
. A first line beginning #! is only required when it is desired to execute the file with the operating system’s shebang mechanism.
8. Conclusion
Whether you’re a beginner exploring the basics of Java programming or an experienced developer seeking to optimize your workflow, mastering the art of launching single-file source-code programs in Java 11 opens up new avenues for innovation and efficiency in your projects.