JShell - Java 9中用於快速原型設計的新REPL工具
REPL代表Read-Eval-Print-Loop。聽起來有點神祕,但它只是程式語言的互動式shell的一個奇特名稱。如今,許多語言已經提供了REPL。即使在JVM Groovy上,Kotlin,Scala和Clojure也已經擁有它。從版本9開始的Java最終有自己的REPL,稱為JShell
好吧,所以Java終於擁有了它新的閃亮的REPL。但它有什麼好處呢?好吧,簡而言之,它允許您將獨立的Java程式碼片段寫入控制檯(READ),立即執行它們(EVAL),然後檢視結果(PRINT)並繼續記住您已編寫的內容(LOOP)。如果您想快速嘗試一段程式碼,草擬演算法,檢查某些方法如何處理異常輸入,為您的部落格帖子建立和測試程式碼片段,它是一個完美的工具。您只需快速嘗試一些一次性程式碼並立即看到結果。最好的部分是 - 它不需要大多數Java樣板。
雖然原型設計和快速程式碼驗證很重要,但還有另一個原因可以讓REPL變得有用。特別是在一個冗長的語言中,如Java等樣板檔案。想象一下,您正在教授Java初學者。你需要寫一個簡單的hello world程式,只打印到控制檯?可以使用帶main方法的類。問題是您需要向學生展示他們現在不需要擔心的許多概念,例如類,方法,靜態,字串陣列等。然後,當您進行更改時,您需要重新編譯並重新執行整個系統。
互動式控制檯不需要使用main類,並立即顯示輸出。您可以嘗試各種結構,並快速檢視結果。您不需要任何IDE。您可以使用最少的設定和對所有高階概念的最少知識開始程式設計,當時只學習一個構造。這種高門檻也導致許多學校和機構放棄Java作為程式語言選擇的介紹。教育方面實際上是JEP222中所述功能的主要動機。
執行JShell
JShell與JDK 9+安裝捆綁在一起。它駐留在JDK \ bin資料夾中。例如在Windows上它可以在這裡:
C:\Program Files\Java\jdk-9.0.4\bin\jshell.exe
要直接從控制檯執行它,請確保將JDK \ bin新增到您的PATH。然後只需執行jshell命令。現在,讓我們使用詳細模式-v,這將有助於我們更好地瞭解幕後發生的事情。或者,您可以直接從bin目錄執行可執行檔案。最後一個選項是使用IDE整合。
如果你使用IntelliJ IDEA,你不必擔心在類路徑上使用JShell,因為IDEA直接在IDE中提供了與JShell開箱即用的出色整合。您將獲得所有有用的功能,例如程式碼完成,語法突出顯示,錯誤檢測等。
若要從IDEA訪問JShell,去工具→JShell控制檯...。
C:\Users\vojtech> jshell -v | Welcome to JShell -- Version 10.0.1 | For an introduction type: /help intro jshell>
現在您處於互動模式,JShell會評估您編寫的任何內容。要退出JShell只需鍵入/exit。
表示式
從JShell開始的最簡單方法是編寫一個簡單的表示式。它可以是一個簡單的數學表示式:
jshell> 7*(3+12) $1 ==> 105 |created scratch variable $1 : <b>int</b>
如您所見,表示式立即被評估,結果將列印到控制檯。無需先宣告任何變數。然而,為方便起見,我們建立了一個名為$ 1的臨時變數,我們可以從現在開始使用它。請注意,變數的型別被推斷為int。
然而,表達不限於如上所述的簡單數學表示式。這不會很有用。你幾乎可以用Java做任何事情。
jshell> Math.sqrt($1)+7 $2 ==> 17.246950765959596
首先,請注意我們能夠使用上一個示例中的$ 1變數。在每個命令之間保留狀態。另一個值得注意的特性是,正如您所看到的,在大多數情況下,分號是可選的。
變數
即使JShell在未將返回值分配給任何變數時為我們宣告變數,通常最好宣告自己的變數。如果僅為了描述性命名。您可以將它們宣告為區域性變數。
jshell> int myVariable = 42
一段時間後,您可能會混淆已經宣告的變數以及它們的值。這是一個特定的命令 - 只需鍵入/vars。
如果您已經使用Java 10,則可以使用ofollow,noindex" target="_blank">var而不是顯式宣告型別 。
方法
如上所述,您可以不需要在任何類中而是直接在根級別宣告變數,您可以對方法執行相同的操作。同樣,您不必擔心任何修飾符,例如public,static或final。您只需以返回型別和方法名稱開頭,並帶有可能的引數:
jshell> String sayHello(String name) { ...> <b>return</b> <font>"Hello, my name is "</font><font>+name; ...> } |created method sayHello(String) jshell> sayHello(</font><font>"Joe"</font><font>) $7 ==> </font><font>"Hello, my name is Joe"</font><font> |created scratch variable $7 : String </font>
在上面的示例中,您可以看到我們聲明瞭一個方法然後呼叫它。請注意,與頂層不同,內部方法和類分號不是可選的。
常見的情況是當方法使用另一個尚未宣告的方法或變數時。它被稱為前向引用,JShell允許您這樣做。但是,在宣告所有依賴項之前,不能使用此類方法。
jshell> String myMethod(String name) { ...> <b>return</b> otherMethodNotDeclared(); ...> } |created method myMethod(String), however, it cannot be invoked until method otherMethodNotDeclared() is declared
與/vars變數類似,您可以列出所有當前宣告的方法/methods。
型別
頂級變數和方法很有用,但通常需要宣告和使用常規類,列舉或介面。你可以像往常一樣,這裡沒有特定的JShell。請記住,在這種情況下需要分號。您可以通過/types列出所有宣告的型別。
jshell> <b>class</b> Person { ...><b>private</b> String name; [more code here] ...> } |created <b>class</b> Person
使用外部程式碼
如上例所示定義所有類是一項繁瑣的任務。更重要的是,您通常希望使用JDK中已有的類甚至您自己的類。
對於JDK類,您可以import照常使用。為方便起見,預設情況下已匯入許多常用類。不僅往常一樣java.lang,而且java.io,java.math,java.util或java.nio.file。您可以列出所有當前匯入/import。
當然,如果JShell無法訪問所需的類,則匯入將毫無用處。
您的類需要在類路徑上。第一種選擇是使用CLASSPATH環境變數。或者您可以在啟動JShell時指定classpath:
jshell --class-path foo-1.0.0.jar
您可以直接從jshell定義類路徑:
jshell> /env -class-path foo-1.0.0.jar
如果您使用的是Java 9模組系統,則可以在啟動JShell時指定要匯入的模組:
jshell --add-modules some.module
例外
好訊息是JShell可以很好地處理異常。每當發生異常時,JShell都會捕獲它,列印它並且您的會話不會終止。很酷的是,與常規Java不同,它不會強制您處理已檢查的異常,這會刪除大量的try-catch樣板。
儲存並載入您的工作
當您在更復雜的用例中使用JShell時,能夠儲存您的工作並在以後繼續使用通常很方便。或者只是儲存您的會話以供將來參考。幸運的是JShell支援使用/save儲存和載入會話/open :
jshell> /save myfile.jsh
jshell> /open myfile.jsh
使用外部編輯器
雖然您可以直接在JShell控制檯中編寫和編輯所有內容,但它並不總是最好的方法。編輯可能繁瑣且繁瑣。使用專用的外部文字編輯器通常更好。要開啟到目前為止在外部編輯器中輸入的所有程式碼段,請鍵入/edit。
您可以配置自己的編輯器,一種方法是傳達環境變數JSHELLEDITOR。或者,您可以直接從JShell設定編輯器。使用-retain選項可在會話之間保留此設定。
jshell> /set editor myEditor -retain
如果您的編輯器未開啟PATH,則需要提供可執行檔案的完整路徑。
以程式設計方式使用JShell
一個非常有趣的選擇是將Java應用程式與JShell整合。您可以以程式設計方式建立JShell例項,然後在您的應用程式中使用它。所有必需的類都在jdk.jshell。首先,您需要建立一個JShell例項,然後您可以使用它來評估程式碼片段。
JShell shell = JShell.create(); List<SnippetEvent> events = shell.;
該eval方法評估程式碼片段並返回在評估期間發生的事件 列表。通過這些,您可以判斷是否發生了一些異常以及程式碼段是否有效。要訪問有關程式碼段本身的詳細資訊,您需要呼叫snippetEvent.snippet()。
JShell shell = JShell.create(); List<SnippetEvent> events = shell.; SnippetEvent event = events.get(0); Snippet snippet = event.snippet(); Snippet.Kind kind = snippet.kind(); String source = snippet.source();
要獲取程式碼段的完整原始碼,您可以使用snippet.source()。