摘要:那本文為什么說,可以不編譯直接執(zhí)行了呢其實(shí),這個(gè)是里新加的一個(gè),目的是使單個(gè)文件的源碼可以無需編譯,直接執(zhí)行。中還提到,在類操作系統(tǒng)下,上面的代碼還可以以形式執(zhí)行。我們再寫一個(gè)例子看下看到?jīng)],我們用寫的代碼居然可以像腳本一樣直接執(zhí)行了。
我們都知道java是靜態(tài)語言,也就是說,如果你想執(zhí)行java程序,就必須先編譯,再執(zhí)行。
那本文為什么說,java可以不編譯直接執(zhí)行了呢?
其實(shí),這個(gè)是OpenJDK11里新加的一個(gè)feature,目的是使單個(gè)文件的java源碼可以無需編譯,直接執(zhí)行。
下面的JEP里對該特性做了詳細(xì)的描述:
http://openjdk.java.net/jeps/330
我們先寫個(gè)小例子實(shí)驗(yàn)下:
$ cat Test.java public class Test { public static void main(String[] args) { System.out.println("hello"); } } $ java Test.java hello
真的可以執(zhí)行,神奇。
JEP 330 中還提到,在類Unix操作系統(tǒng)下,上面的代碼還可以以 "Shebang" 形式執(zhí)行。
我們再寫一個(gè)例子看下:
$ 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
看到?jīng)],我們用java寫的代碼居然可以像shell腳本一樣直接執(zhí)行了。
那這一切在JVM中又是怎么實(shí)現(xiàn)的呢?靜態(tài)語言為什么也可以像腳本一樣動(dòng)態(tài)執(zhí)行了呢?
下面我們來看下對應(yīng)的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; }
當(dāng)我們要執(zhí)行的java程序是java源文件時(shí),該方法中的mode就會(huì)被設(shè)置為LM_SOURCE。
pwhat指針指向的是我們最終要執(zhí)行的帶main方法的java類,由上我們可以看到,在mode為LM_SOURCE時(shí),最終執(zhí)行的java類并不是我們提供的java源文件對應(yīng)的java類,而是SOURCE_LAUNCHER_MAIN_ENTRY宏定義的java類。
我們看下這個(gè)宏對應(yīng)的java類是什么:
// src/java.base/share/native/libjli/java.c #define SOURCE_LAUNCHER_MAIN_ENTRY "jdk.compiler/com.sun.tools.javac.launcher.Main"
由上可見,它是jdk.compiler模塊里的一個(gè)類,java命令最終執(zhí)行的main方法就是這個(gè)類里的main方法。
那這個(gè)main方法的參數(shù)是什么呢?
其實(shí)就是我們提供的java源文件,不過為了更加明確,我們還是通過以下方式驗(yàn)證下:
$ _JAVA_LAUNCHER_DEBUG=1 java Test.java ----_JAVA_LAUNCHER_DEBUG---- # 省略無關(guān)信息 Source is "jdk.compiler/com.sun.tools.javac.launcher.Main" App"s argc is 1 argv[ 0] = "Test.java" # 省略無關(guān)信息 ----_JAVA_LAUNCHER_DEBUG---- hello
如果我們在啟動(dòng)java之前,設(shè)置了_JAVA_LAUNCHER_DEBUG環(huán)境變量,JVM內(nèi)部就會(huì)輸出一些運(yùn)行時(shí)的數(shù)據(jù)來供我們調(diào)試,比如,由上面的輸出我們可以看到,java命令將要執(zhí)行的帶main方法的java類為jdk.compiler/com.sun.tools.javac.launcher.Main,其參數(shù)為Test.java,正好和我們上文中分析的是一樣的。
也就是說,當(dāng)我們以源文件形式執(zhí)行java命令時(shí),最終調(diào)用的main方法是jdk.compiler/com.sun.tools.javac.launcher.Main里的main方法,其參數(shù)為我們要執(zhí)行的java源文件。
下面我們再來看下這個(gè)main方法究竟是如何執(zhí)行我們的源文件的:
// 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); // 我們要執(zhí)行的源文件 ... 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) { ... } } }
在這里我們只列出了相關(guān)方法的大致邏輯,不過已經(jīng)足夠能看出,它到底是怎么執(zhí)行的了。
我們要執(zhí)行的源碼先被java的compiler編譯,然后又調(diào)用了其main方法繼續(xù)執(zhí)行我們寫的邏輯。
原來是如此簡單。
不過,java源碼可動(dòng)態(tài)執(zhí)行的特性還是給我們留下了很多想像空間,雖然其實(shí)現(xiàn)機(jī)制很粗暴,但對用戶來說還算是友好的。
希望本篇文章能給各位同學(xué)帶來一些收獲。
完。
更多原創(chuàng)文章,請關(guān)注我微信公眾號:
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/76052.html
摘要:比如說,就是復(fù)姓,名字為的類別則是復(fù)姓,名字為的類別。先介紹的機(jī)制基本原則需要將類文件切實(shí)安置到其所歸屬之所對應(yīng)的相對路徑下。把源代碼文件,文件和其他文件有條理的進(jìn)行一個(gè)組織,以供來使用??梢允褂猛ㄅ浞砟诚滤械?,不包括子目錄。 一些人用了一陣子的Java,可是對于 Java 的 package 跟 import 還是不太了解。很多人以為原始碼 .java 文件中的 import...
摘要:虛擬機(jī)學(xué)習(xí)是一個(gè)虛構(gòu)出來的計(jì)算機(jī)有自己的處理器堆棧寄存器以及相應(yīng)的指令系統(tǒng)等。類裝載器子系統(tǒng)涉及虛擬機(jī)的其它組成部分和來自庫的類。運(yùn)行中的程序的每一個(gè)線程都是一個(gè)獨(dú)立的虛擬機(jī)執(zhí)行引擎的實(shí)例。 Java虛擬機(jī)學(xué)習(xí) JVM JVM是一個(gè)虛構(gòu)出來的計(jì)算機(jī),有自己的處理器,堆棧,寄存器以及相應(yīng)的指令系統(tǒng)等。JVM是JRE的一部分,通過在實(shí)際的計(jì)算機(jī)上仿真模擬各種計(jì)算機(jī)功能,這樣就能使Java在...
摘要:需要注意的地方輸入法狀態(tài)調(diào)整為英文狀態(tài)代碼的縮進(jìn)不要忘記分號下面圖片標(biāo)注內(nèi)容。語句語句是程序最小的一個(gè)執(zhí)行單位,像一個(gè)指令,程序中,必須使用一個(gè)英文分號結(jié)束一條語句。建議,第一個(gè)簡單的程序,我已經(jīng)詳細(xì)的為你做了演練與解釋。 在上一篇文章 【[準(zhǔn)備編譯環(huán)境】]()中我們完成了 Java 編譯環(huán)境的搭建,這篇文章內(nèi)容主要是來教你怎么開始編寫第一個(gè) Java 程序,并運(yùn)行它。 分為兩個(gè)步驟,...
摘要:而字節(jié)碼運(yùn)行在之上,所以不用關(guān)心字節(jié)碼是在哪個(gè)操作系統(tǒng)編譯的,只要符合規(guī)范,那么,這個(gè)字節(jié)碼文件就是可運(yùn)行的。好處防止內(nèi)存中出現(xiàn)多份同樣的字節(jié)碼安全性角度特別說明類加載器在成功加載某個(gè)類之后,會(huì)把得到的類的實(shí)例緩存起來。 前言 只有光頭才能變強(qiáng) JVM在準(zhǔn)備面試的時(shí)候就有看了,一直沒時(shí)間寫筆記。現(xiàn)在到了一家公司實(shí)習(xí),閑的時(shí)候就寫寫,刷刷JVM博客,刷刷電子書。 學(xué)習(xí)JVM的目的也很簡單...
閱讀 1084·2021-09-29 09:35
閱讀 4665·2021-09-22 15:24
閱讀 1461·2021-07-25 21:37
閱讀 2192·2019-08-30 14:17
閱讀 976·2019-08-30 13:56
閱讀 2420·2019-08-29 17:07
閱讀 1280·2019-08-29 12:44
閱讀 2714·2019-08-26 18:26