摘要:那本文為什么說,可以不編譯直接執行了呢其實,這個是里新加的一個,目的是使單個文件的源碼可以無需編譯,直接執行。中還提到,在類操作系統下,上面的代碼還可以以形式執行。我們再寫一個例子看下看到沒,我們用寫的代碼居然可以像腳本一樣直接執行了。
我們都知道java是靜態語言,也就是說,如果你想執行java程序,就必須先編譯,再執行。
那本文為什么說,java可以不編譯直接執行了呢?
其實,這個是OpenJDK11里新加的一個feature,目的是使單個文件的java源碼可以無需編譯,直接執行。
下面的JEP里對該特性做了詳細的描述:
http://openjdk.java.net/jeps/330
我們先寫個小例子實驗下:
$ cat Test.java public class Test { public static void main(String[] args) { System.out.println("hello"); } } $ java Test.java hello
真的可以執行,神奇。
JEP 330 中還提到,在類Unix操作系統下,上面的代碼還可以以 "Shebang" 形式執行。
我們再寫一個例子看下:
$ cat Test #!/usr/bin/java --source 12 public class Test { public static void main(String[] args) { System.out.println("hello"); } } $ chmod +x Test $ ./Test hello
看到沒,我們用java寫的代碼居然可以像shell腳本一樣直接執行了。
那這一切在JVM中又是怎么實現的呢?靜態語言為什么也可以像腳本一樣動態執行了呢?
下面我們來看下對應的JVM源碼:
// src/java.base/share/native/libjli/java.c static jboolean ParseArguments(int *pargc, char ***pargv, int *pmode, char **pwhat, int *pret, const char *jrepath) { ... if (mode == LM_SOURCE) { ... *pwhat = SOURCE_LAUNCHER_MAIN_ENTRY; ... } ... *pmode = mode; return JNI_TRUE; }
當我們要執行的java程序是java源文件時,該方法中的mode就會被設置為LM_SOURCE。
pwhat指針指向的是我們最終要執行的帶main方法的java類,由上我們可以看到,在mode為LM_SOURCE時,最終執行的java類并不是我們提供的java源文件對應的java類,而是SOURCE_LAUNCHER_MAIN_ENTRY宏定義的java類。
我們看下這個宏對應的java類是什么:
// src/java.base/share/native/libjli/java.c #define SOURCE_LAUNCHER_MAIN_ENTRY "jdk.compiler/com.sun.tools.javac.launcher.Main"
由上可見,它是jdk.compiler模塊里的一個類,java命令最終執行的main方法就是這個類里的main方法。
那這個main方法的參數是什么呢?
其實就是我們提供的java源文件,不過為了更加明確,我們還是通過以下方式驗證下:
$ _JAVA_LAUNCHER_DEBUG=1 java Test.java ----_JAVA_LAUNCHER_DEBUG---- # 省略無關信息 Source is "jdk.compiler/com.sun.tools.javac.launcher.Main" App"s argc is 1 argv[ 0] = "Test.java" # 省略無關信息 ----_JAVA_LAUNCHER_DEBUG---- hello
如果我們在啟動java之前,設置了_JAVA_LAUNCHER_DEBUG環境變量,JVM內部就會輸出一些運行時的數據來供我們調試,比如,由上面的輸出我們可以看到,java命令將要執行的帶main方法的java類為jdk.compiler/com.sun.tools.javac.launcher.Main,其參數為Test.java,正好和我們上文中分析的是一樣的。
也就是說,當我們以源文件形式執行java命令時,最終調用的main方法是jdk.compiler/com.sun.tools.javac.launcher.Main里的main方法,其參數為我們要執行的java源文件。
下面我們再來看下這個main方法究竟是如何執行我們的源文件的:
// com.sun.tools.javac.launcher.Main public class Main { ... public static void main(String... args) throws Throwable { try { new Main(System.err).run(VM.getRuntimeArguments(), args); } catch (Fault f) { ... } } ... public void run(String[] runtimeArgs, String[] args) throws Fault, InvocationTargetException { Path file = getFile(args); // 我們要執行的源文件 ... String mainClassName = compile(file, getJavacOpts(runtimeArgs), context); String[] appArgs = Arrays.copyOfRange(args, 1, args.length); execute(mainClassName, appArgs, context); } ... private void execute(String mainClassName, String[] appArgs, Context context) throws Fault, InvocationTargetException { ... try { Class> appClass = Class.forName(mainClassName, true, cl); Method main = appClass.getDeclaredMethod("main", String[].class); ... main.invoke(0, (Object) appArgs); } catch (ClassNotFoundException e) { ... } } }
在這里我們只列出了相關方法的大致邏輯,不過已經足夠能看出,它到底是怎么執行的了。
我們要執行的源碼先被java的compiler編譯,然后又調用了其main方法繼續執行我們寫的邏輯。
原來是如此簡單。
不過,java源碼可動態執行的特性還是給我們留下了很多想像空間,雖然其實現機制很粗暴,但對用戶來說還算是友好的。
希望本篇文章能給各位同學帶來一些收獲。
完。
更多原創文章,請關注我微信公眾號:
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/76052.html
摘要:比如說,就是復姓,名字為的類別則是復姓,名字為的類別。先介紹的機制基本原則需要將類文件切實安置到其所歸屬之所對應的相對路徑下。把源代碼文件,文件和其他文件有條理的進行一個組織,以供來使用。可以使用通配符,代表某下所有的,不包括子目錄。 一些人用了一陣子的Java,可是對于 Java 的 package 跟 import 還是不太了解。很多人以為原始碼 .java 文件中的 import...
摘要:需要注意的地方輸入法狀態調整為英文狀態代碼的縮進不要忘記分號下面圖片標注內容。語句語句是程序最小的一個執行單位,像一個指令,程序中,必須使用一個英文分號結束一條語句。建議,第一個簡單的程序,我已經詳細的為你做了演練與解釋。 在上一篇文章 【[準備編譯環境】]()中我們完成了 Java 編譯環境的搭建,這篇文章內容主要是來教你怎么開始編寫第一個 Java 程序,并運行它。 分為兩個步驟,...
摘要:而字節碼運行在之上,所以不用關心字節碼是在哪個操作系統編譯的,只要符合規范,那么,這個字節碼文件就是可運行的。好處防止內存中出現多份同樣的字節碼安全性角度特別說明類加載器在成功加載某個類之后,會把得到的類的實例緩存起來。 前言 只有光頭才能變強 JVM在準備面試的時候就有看了,一直沒時間寫筆記。現在到了一家公司實習,閑的時候就寫寫,刷刷JVM博客,刷刷電子書。 學習JVM的目的也很簡單...
閱讀 1075·2021-09-29 09:35
閱讀 4621·2021-09-22 15:24
閱讀 1449·2021-07-25 21:37
閱讀 2178·2019-08-30 14:17
閱讀 965·2019-08-30 13:56
閱讀 2411·2019-08-29 17:07
閱讀 1251·2019-08-29 12:44
閱讀 2705·2019-08-26 18:26