在JDK11中執行單檔案程式碼
翻譯:叩丁狼教育吳嘉俊
JEP330-啟動單檔案程式碼程式(Launch Single-File Source-Code Programs)是即將更新的JDK11(18.9)版本中一個很不錯的功能。這個功能允許你直接使用java解析器執行java程式碼。java檔案會在記憶體中執行編譯並且直接執行。唯一的約束在於所有相關的類必須定義在東一個java檔案中。
這個特徵非常適合剛剛準備學習java的童鞋,或者想快速嘗試一些簡單程式碼。這個功能和jshell會成為所有java初學者的最強大的工具。不僅僅如此,所有的成熟的開發,可以使用這個工具來快速驗證和學習新的API。
在本文中,我不會具體去探討這個功能是如何實現的,相反,我們會集中精力在如何使用這個功能。好了,我們仍然從Hello World示例開始!
最基礎的案例
把以下程式碼儲存到Hello.java檔案中:
public class HelloWorld{
public static void main(String[] args){
System.out.println("Hello World!!!");
}
}
我們將會按照下面的方法來執行上面的程式碼:
PS G:\samples\java11\single-file> java HelloWorld.java
Hello World!!!
在上面的例子,我們僅僅只是在一個類中包含了一個main方法。我們直接使用java命令去執行這個.java檔案。如果這個檔案不是以.java結尾,我們可以使用—source引數來執行,這個待會會看到。
包含命令列引數
接下來的案例,我們傳入一個引數,允許給所有人打招呼:
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檔案中。注意,這個檔名字和我們的類的名字不匹配。我們按照如下命令執行:
PS G:\samples\java11\single-file> java HelloGreeting.Java sana
Hello sana!!
任何一個跟在檔名後面的引數都被作為方法的引數傳入方法執行。我們把HelloGreeting.java直接重新命名為greeting(注意,沒有.java字尾),我們再次執行:
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結尾的情況下,java編譯器會嘗試直接使用傳入的名稱作為類名去尋找.class檔案。在這種情況下,我們需要使用—source選項:
PS G:\samples\java11\single-file> java --source 11 greeting sana
Hello sana!!
有了—source選項,我們可以非常方便的演示在JDK10中寫的程式碼不能再JDK9中執行的情況:
public class Java10Compatible{
public static void main(String[] args){
var message = "Hello world";
System.out.println(message);
}
}
我們分別在JDK10和JDK9下執行:
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
一個檔案中包含多個類
文章開頭我就提到,這個特性只是要求所有需要執行的程式碼是在同一個java檔案中即可,而沒有規定在這個java檔案中只能有一個類。我們下面就來看看在一個java檔案中包含多個類的情況:
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;
}
}
我們來執行這個程式碼:
PS G:\samples\java11\single-file> java .\SimpleInterest.java 1000 2 10
Simple Interest is: 200.0
在這個檔案中,我們定義了多個類,但是在執行的時候,java編譯器會執行這個檔案中第一個類中的main方法(注:意思是,這個檔案中的第一個類需要包含main方法,並且這個main方法作為執行的方法)
使用模組
在記憶體中編譯和執行的類會作為一個添加了—add-modules=ALL-DEFAULT引數的匿名模組。這允許程式碼使用不同的模組,而不需要額外的定義module-info.java。
下面我們就來看一個使用了新HTTP Client API的HTTP請求案例。HTTP Client API是Java9作為獨立的模組,放在java.net.http模組中的。下面的程式碼演示了這個特性:
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());
}
}
我們可以直接使用命令執行:
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",
"avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/calebogden/128.jpg"},
{"id":2,"first_name":"Janet","last_name":"Weaver",
"avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg"},
{"id":3,"first_name":"Emma","last_name":"Wong",
"avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/olegpogodaev/128.jpg"}]}
這個特性允許我們快速的實驗一個新的模組,而不需要建立額外的module-info檔案。
Shebang檔案
在本小節中,我們會建立一個shebang檔案。Shebang檔案是Unix系統中常見的檔案,以#!/path/to/executable作為檔案的開頭第一行,可以作為指令碼小程式直接執行一段程式碼。
我們來建立一個shebang檔案:
#!/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命名規範的時候,就可以建立shebang檔案來執行。比如我們上面的程式碼就可以儲存在一個叫simpleInterest的檔案中。我們需要按照下面的方式來執行:
[email protected] /g/samples/java11/single-file (master)
$ ./simpleInterest 1000 20 2
Simple Interest is: 400.0
在windows下,我們只能使用bash shell來執行。當然,還有諸如Cygwin,Windows 10 Ubuntu Support等工具來執行。
注:JEP330中對Shebang的說明:
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 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 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”.
原文:https://sanaulla.info/2018/07/05/launch-single-file-source-code-programs-in-jdk-11/