Learnitweb

Java 11 Single-File Source-Code Programs

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.