成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Java也可以不用編譯直接執(zhí)行了?

Benedict Evans / 2319人閱讀

摘要:那本文為什么說,可以不編譯直接執(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

相關(guān)文章

  • 【轉(zhuǎn)】Java的package和import機(jī)制

    摘要:比如說,就是復(fù)姓,名字為的類別則是復(fù)姓,名字為的類別。先介紹的機(jī)制基本原則需要將類文件切實(shí)安置到其所歸屬之所對應(yīng)的相對路徑下。把源代碼文件,文件和其他文件有條理的進(jìn)行一個(gè)組織,以供來使用??梢允褂猛ㄅ浞砟诚滤械?,不包括子目錄。 一些人用了一陣子的Java,可是對于 Java 的 package 跟 import 還是不太了解。很多人以為原始碼 .java 文件中的 import...

    anRui 評論0 收藏0
  • Java虛擬機(jī)學(xué)習(xí)

    摘要:虛擬機(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在...

    RobinTang 評論0 收藏0
  • 3. 第一個(gè) Java 程序 - Hello World 【連載 3】

    摘要:需要注意的地方輸入法狀態(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è)步驟,...

    shevy 評論0 收藏0
  • 學(xué)習(xí)JVM是如何從入門到放棄的?

    摘要:而字節(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的目的也很簡單...

    Joyven 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<