JShell 是在 JDK 9 中首次引入的,以 Kulla 实现的 Java Enhancement Proposal (JEP) 222 规范的一部分。很多编程语言如 JavaScript、Python、Ruby 等,提供了非常易用的命令行执行工具,但 Java 一直缺失此功能。因此 JDK 9 引入了 Java shell 工具 —— JShell。
在之前的 文章 中我们曾经讨论了 JShell 的一些基础知识,这篇文字中我们主要聊一些高级的概念。
在 Java 中,我们是没法对一个变量进行重新声明的。但是有了 JShell,我们可以随时在需要的时候对一个变量重新进行定义,包括原生类型以及引用类型变量。
示例:
jshell> String str="Hello" str ==> "JShell"
jshell> Integer str=10 str ==> 10
在 JShell 命令行中可以将任意表达式计算的结果赋值给变量,尽管你并没有显式的赋值。这样的变量称为临时变量。例如:
jshell> "Hello"+"JShell" $1 ==> "HelloJShell"
请注意为了让 JShell 知道变量类型以及表达式计算的详细信息,我们可以设置 feeback 为详细模式:
/set feedback verbose
jshell> 60+10 $2 ==> 70 | created scratch variable $21 : int
要退出详细模式,只需转回正常模式即可:
/set feedback normal
JShell 的前向引用 (Forward referencing) 可以让你在未定义某些方法时仍然可以调用。例如,假设我们有一个名为 greet() 的方法(如下所示)。而 greet() 内部调用了另外一个尚未定义 greetHelloWorld() 方法,这种情况下我们仍可以正确的创建 greet() 方法,但只有在 greetHelloWorld 方法创建后才可以被调用。这就是 JShell 的前向引用。
示例:
jshell> public void greet(){ ...> greetHelloWorld();} | created method greet(), however, it cannot be invoked until method greetHelloWorld() is declared jshell> greet() | attempted to call method greet() which cannot be invoked until method greetHelloWorld() is declared
jshell> public void greetHelloWorld(){ ...> System.out.println("Hello World");} | created method greetHelloWorld()
jshell> greet() Hello World
示例:
jshell> int divide(int a,int b) throws IOException{ ...> if(b==0){ ...> throw new IOException(); ...> } ...> return a/b; ...> } | created method divide(int,int)
jshell> divide(1,0) | java.io.IOException thrown: | at divide (#2:3) | at (#3:1)
注意这里我们并没有捕获任何关于 divide 方法的异常,但是 JShell 会帮我们处理好。同时这里我们也没有引入 IOException 类,但代码的编译和执行都没有任何问题。原因是 JShell 的整个会话过程中会自动的引入一些常用的包,你可以使用 /imports 命令来查看 JShell 默认引用的包:
jshell> /imports | import java.io.* | import java.math.* | import java.net.* | import java.nio.file.* | import java.util.* | import java.util.concurrent.* | import java.util.function.* | import java.util.prefs.* | import java.util.regex.* | import java.util.stream.* | import java.io.IOException
默认情况下 JShell 会话中的所有指令都是不被持久化的,这些指令是易失的,当用户退出 JShell 会话时就会丢失。
但是 JShell 提供了在特定的会话中保存所有指令信息的方法,你可以在另外一个新的会话中使用这些指令。当用户需要保存一些有用的代码片段时候,这个功能是很好用的。
示例:
jshell> String s="Hello" s ==> "Hello" jshell> int i=100; i ==> 100
jshell> /save C:/data/mySession.jsh
jshell> /exit | Goodbye λ jshell | Welcome to JShell -- Version 9.0.4 | For an introduction type: /help intro
jshell> /vars
jshell> /open C:/Data/mySession.jsh
jshell> /vars | String s = "Hello" | int i = 100
有很多的第三方开源库,一般开发者需要将这些库放到项目的类路径才可以使用。但是在 JShell 中,使用这些三方库更简单。
例如我们想使用 Apache Commons Lang 中的字符串工具包,可以使用如下语法来使用第三方库包:
(译者注:其实并没有那么方便)
shell> /env --class-path <Relative Path of lib from where JShell is run>
jshell> /env --class-path ../lib/commons-lang3-3.8.1.jar | Setting new options and restoring state.
import org.apache.commons.lang3.StringUtils;
jshell> System.out.println(StringUtils.isEmpty("")) true
jshell> System.out.println(StringUtils.isEmpty("hello")) false
JShell 包含很多很有用的命令,这些方法可以加速代码的测试,例如:
/history - Prints all commands executed on JShell (Java Commands+ JShell specific commands)
示例:
jshell> String s ="Hello" s ==> "Hello" jshell> class Employee{ ...> } | created class Employee jshell> /vars | String s = "Hello" jshell> /history String s ="Hello" class Employee{ } /vars /history
/list - Prints all JAVA related commands executed in JShell. Notice that this list the command in Numerical order of each command identifier. This identifier can be used to execute certain construct again.
示例:
jshell> /list 1 : String s ="Hello"; 2 : class Employee{ }
jshell> /1 String s ="Hello"; s ==> "Hello"
/reset - Resets the state of current JShell session.
CTRL+R - For searching a particular command
CTRL+S - Performing Forward Search
CTRL+C - To exit from JShell session
/exit - To exit from JShell session
/vars - To list all variables inside current JShell session
/imports - To list all imports inside current JShell session
/help - To know more about JShell specific commands
JShell 可以使用 Tab 键来达到代码自动完成的功能。
示例:
除了这些,你还可以在 JShell 中查看相关包的文档:
jshell> java.io io Signatures: java.io
<press tab again to see documentation>
在开发过程中我们经常会需要修改之前执行的命令,JShell 可以很方便的实现。
示例:
/edit - Edit all constructs in current JShell session
/edit 1 - Edit only 1st construct (see from /list) in current JShell session
/edit Person - Edit only Person class in current JShell session
JDK 提供 API 用来访问 JShell ,这些 API 可以浏览 JavaDoc 了解详情。
这篇文字里我们介绍了一些 JShell 的高级特性,但这并非全部,建议开发者通过 JShell 的文档了解更多信息。
文章翻译自: https://developers.redhat.com/blog/2019/04/05/10-things-developers-need-to-know-about-jshell/