Статьи

Запустите однофайловые программы с исходным кодом в JDK 11

JEP 330 — Запуск однофайловых программ с исходным кодом — одна из захватывающих функций в готовящемся выпуске JDK 11 (18.9). Эта функция позволяет выполнять исходный код Java непосредственно с помощью интерпретатора java . Исходный код компилируется в памяти и затем исполняется интерпретатором. Ограничение состоит в том, что все классы должны быть определены в одном файле.

Эта функция особенно полезна для тех, кто начинает изучать Java и хочет попробовать простые программы. Эта функция вместе с jshell станет jshell инструментов для любого новичка в изучении языка. Не только они, но и профессионалы могут также использовать эти инструменты для изучения новых языковых изменений или опробовать неизвестный API.

В этом посте я не буду вдаваться в подробности того, как реализована эта функция, вместо этого я сосредоточусь на использовании этой функции, пробуя различные образцы. Начнем с самого простого примера, как обычно, Hello World!

Простейший пример

Приведенный ниже код сохраняется в файле HelloWorld.java

1
2
3
4
5
public class HelloWorld{
    public static void main(String[] args){
        System.out.println("Hello World!!!");
    }
}

Я выполню приведенный выше код, как показано ниже:

1
2
PS G:\samples\java11\single-file> java HelloWorld.java
Hello World!!!

В приведенном выше примере есть только один класс, и он содержит метод main . При выполнении кода с использованием java мы должны передать ему имя файла, заканчивающееся расширением .java . Если имя файла не заканчивается расширением .java тогда мы должны использовать опцию --source как мы увидим в следующем примере.

С аргументами командной строки

Давайте расширим программу Hello Worl, чтобы создать индивидуальное приветствие для каждого человека:

1
2
3
4
5
6
7
8
9
public class Greeting{
    public static void main(String[] args){
        if ( args == null || args.length < 1 ){
            System.err.println("Name required");
            System.exit(1);
        }
        System.out.println(String.format("Hello %s!!", args[0]));
    }
}

Позволяет сохранить приведенный выше код в файле с именем HelloGreeting.java . Обратите внимание, что имя файла не совпадает с именем открытого класса. Давайте запустим приведенный выше код, используя:

1
2
PS G:\samples\java11\single-file> java HelloGreeting.Java sana
Hello sana!!

Любые аргументы, которые будут переданы в код, как указано после имени исполняемого файла. Давайте переименуем HelloGreeting.java в просто greeting и попробуем выполнить, используя тот же подход:

1
2
3
PS G:\samples\java11\single-file> java greeting sana
Error: Could not find or load main class greeting
Caused by: java.lang.ClassNotFoundException: greeting

Вы можете видеть, что в отсутствие .java интерпретатор ищет скомпилированный класс по имени, указанному в качестве аргумента. В таких случаях нам нужно использовать параметр --source как показано ниже:

1
2
PS G:\samples\java11\single-file> java --source 11 greeting sana
Hello sana!!

Позвольте мне показать вам, как код, написанный для JDK 10, не будет работать для JDK 9, когда мы используем опцию --source :

1
2
3
4
5
6
public class Java10Compatible{
    public static void main(String[] args){
        var message = "Hello world";
        System.out.println(message);
    }
}

Давайте выполним вышеуказанное для JDK 10 и JDK 9, как показано ниже:

01
02
03
04
05
06
07
08
09
10
PS G:\samples\java11\single-file> java --source 10 Java10Compatible.java
Hello world
PS G:\samples\java11\single-file> java --source 9 Java10Compatible.java
.\Java10Compatible.java:3: error: cannot find symbol
        var message = "Hello world";
        ^
  symbol:   class var
  location: class Java10Compatible
1 error
error: compilation failed

Несколько классов в одном файле

Как я упоминал ранее, эта функция поддерживает запуск кода, который находится в одном файле, нет ограничений на количество классов в файле. Давайте посмотрим на пример кода, который содержит два класса:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
public class SimpleInterest{
    public static void main(String[] args){
        if ( args == null || args.length < 3 ){
            System.err.println("Three arguments required: principal, rate, period");
            System.exit(1);
        }
        int principal = Integer.parseInt(args[0]);
        int rate = Integer.parseInt(args[1]);
        int period = Integer.parseInt(args[2]);
        double interest = Maths.simpleInterest(principal, rate, period);
        System.out.print("Simple Interest is: " + interest);
    }
}
 
public class Maths{
 
    public static double simpleInterest(int principal, int rate, int period){
        return ( principal * rate * period * 1.0 ) / 100;
    }
}

Давайте запустим это:

1
2
PS G:\samples\java11\single-file> java .\SimpleInterest.java 1000 2 10
Simple Interest is: 200.0

В случае файла, в котором определено более одного класса, первый класс должен содержать метод main, а интерпретатор после компиляции в памяти будет использовать первый класс для запуска выполнения.

Использование модулей

Скомпилированный код в памяти запускается как часть неназванного модуля с параметром --add-modules=ALL-DEFAULT . Это позволяет коду использовать разные модули без необходимости явного объявления зависимости с помощью module-info.java

Давайте посмотрим на код, который выполняет HTTP-вызов с использованием новых API-интерфейсов HTTP Client. Эти API, которые были представлены в Java 9 в качестве функции инкубатора, были перемещены из инкубатора в модуль java.net.http . Пример кода:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
import java.net.http.*;
import java.net.http.HttpResponse.BodyHandlers;
import java.net.*;
import java.io.IOException;
 
public class ExternalModuleDepSample{
    public static void main(String[] args) throws Exception{
        HttpClient client = HttpClient.newBuilder().build();
        HttpRequest request = HttpRequest.newBuilder()
            .GET()
            .uri(URI.create("https://reqres.in/api/users"))
            .build();
 
        HttpResponse<String> response =
            client.send(request, BodyHandlers.ofString());
        System.out.println(response.statusCode());
        System.out.println(response.body());    
    }
}

Мы можем запустить приведенный выше код, выполнив следующую команду:

1
2
3
4
5
6
7
8
9
PS G:\samples\java11\single-file>java ExternalModuleDepSample.java
200
{"page":1,"per_page":3,"total":12,"total_pages":4,
"data":[{"id":1,"first_name":"George","last_name":"Bluth",
{"id":2,"first_name":"Janet","last_name":"Weaver",
{"id":3,"first_name":"Emma","last_name":"Wong",

Это позволяет нам быстро тестировать новые функции в разных модулях без необходимости создавать модули, файлы module-info модулях и так далее.

Шебанг файлы

В этом разделе мы рассмотрим создание файлов shebang. Файлы Shebang — это те файлы, которые могут быть выполнены непосредственно в системах Unix, предоставив исполнителю синтаксис #!/path/to/executable в качестве первой строки файла.

Давайте создадим файл shebang:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/g/jdk-11/bin/java --source 11
 
public class SimpleInterest{
    public static void main(String[] args){
        if ( args == null || args.length < 3 ){
            System.err.println("Three arguments required: principal, rate, period");
            System.exit(1);
        }
        int principal = Integer.parseInt(args[0]);
        int rate = Integer.parseInt(args[1]);
        int period = Integer.parseInt(args[2]);
        double interest = Maths.simpleInterest(principal, rate, period);
        System.out.print("Simple Interest is: " + interest);
    }
}
 
public class Maths{
 
    public static double simpleInterest(int principal, int rate, int period){
        if ( rate > 100 ){
            System.err.println("Rate of interest should be <= 100. But given values is " + rate);
            System.exit(1);
        }
        return ( principal * rate * period * 1.0 ) / 100;
    }
}

Опция источника в шебанге используется в тех случаях, когда имя файла не соответствует стандартному соглашению об именах файлов Java. В нашем случае мы сохранили приведенный выше код в файле с именем simpleInterest и мы можем запустить его так:

1
2
3
sanaulla@Sana-Laptop  /g/samples/java11/single-file (master)
$ ./simpleInterest 1000 20 2
Simple Interest is: 400.0

На компьютерах с Windows я использовал оболочку bash, которая поставляется с установкой git . Есть несколько других способов, таких как Cygwin, поддержка Windows 10 Ubuntu и так далее.

Исходный код для этого можно найти здесь .

Опубликовано на Java Code Geeks с разрешения Мохамеда Санауллы, партнера нашей программы JCG . См. Оригинальную статью здесь: Запустите однофайловые программы с исходным кодом в JDK 11

Мнения, высказанные участниками Java Code Geeks, являются их собственными.