bigywater.tech
Java · 270

JShell:用交互式会话验证 Java 想法

JShell 让你不写类、不写 main 就能验证一个想法。它怎么处理前向引用、重新定义、错误和异常?怎么用它试第三方库?它的 API 能做什么?这篇用 jshell 命令行与 jdk.jshell API 逐项真实运行。

$N
没有名字的表达式结果自动保存成的变量
1038 ms
jshell 启动并计算 1+1 再退出(java -version 为 26 ms)
509 ms
js.stop() 中断 while (true) 的耗时

表达式与变量

实测 · 本机执行输出
  jshell>  $1 ==> 2
  jshell>  $2 ==> 6
  jshell>  $3 ==> 3
  jshell>  x ==> 10
  jshell>  $5 ==> 20
  jshell>  x ==> "now a string"
  jshell>  x ==> "now a string"
  jshell>  d ==> 0.0
  jshell>  big ==> -2147483648

前向引用、重新定义与错误恢复

实测 · 本机执行输出
  jshell>  |  created method f(), however, it cannot be invoked until method g() is declared
  jshell>  |  attempted to call method f() which cannot be invoked until method g() is declared
  jshell>  |  created method g()
  jshell>  $4 ==> 42
  jshell>  |  modified method g()
  jshell>  $6 ==> 101
  jshell>  |  Error:
  |  incompatible types: java.lang.String cannot be converted to int
  |  int y = "bad";
  |          ^---^
  jshell>  |  created method h(), however, it cannot be invoked until variable undefinedVar is declared
  jshell>  |  Exception java.lang.ArithmeticException: / by zero
  |        at (#8:1)
  jshell>  z ==> 0
  jshell>  |  Exception java.lang.RuntimeException: boom
  |        at (#10:1)
  jshell>  sx ==> 1
  jshell>  px ==> 2
  jshell>  fx ==> 3
  jshell>  sx ==> 5
  jshell>  |  Goodbye

命令:/vars /methods /list /drop /reset

实测 · 本机执行输出
  jshell>  a ==> 1
  jshell>  s ==> "x"
  jshell>  |  created method twice(int)
  jshell>  |  created class P
  jshell>  |  created interface I
  jshell>  |  created enum E
  jshell>  |  created record R
  jshell>  |    int a = 1
  |    String s = "x"
  jshell>  |    int twice(int)
  jshell>  |    class P
  |    interface I
  |    enum E
  |    record R
  jshell>  |    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.*
  jshell>
  1 : int a = 1;
  2 : String s = "x";
  3 : int twice(int n) { return n * 2; }
  4 : class P { int v; }
  5 : interface I {}
  6 : enum E { A, B }
  7 : record R(int q) {}
  jshell>  |  dropped method twice(int)
  jshell>
  jshell>  |  Resetting state.
  jshell>
  jshell>  |  Goodbye

启动、类路径与默认导入

实测 · 本机执行输出
  jshell 启动并计算 1+1 再退出:1038 ms(5 次中位);对照 java -version 26 ms
  默认导入(/imports): |    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.*
  --class-path 加载 guava:
    jshell>  l ==> [3, 1, 2]
    jshell>  $3 ==> [1, 2, 3]
  不给 --class-path 导入 guava: jshell>  |  Error: | |  package com.google.common.collect does not exist | jshell>  |  Error:
  jshell -s(静默反馈)下 println 的输出: ->  hi | ->  line1 | ->

JShell API

实测 · 本机执行输出
  int a = 6                            → kind=VAR                status=VALID                  value=6
  a * 7                                → kind=VAR                status=VALID                  value=42
  int f() { return h() + 1; }          → kind=METHOD             status=RECOVERABLE_DEFINED    value=null
  int h() { return 41; }               → kind=METHOD             status=VALID                  value=null
  int h() { return 41; }               → kind=METHOD             status=VALID                  value=null
  f()                                  → kind=VAR                status=VALID                  value=42
  int a = "x";                         → kind=VAR                status=REJECTED               value=null,诊断:incompatible types: java.lang.String cannot be converted to int
  int a = "x";                         → kind=VAR                status=OVERWRITTEN            value=null
  String s = null; s.length()          → kind=VAR                status=VALID                  value=null
  String s = null; s.length()          → kind=VAR                status=VALID                  value=null,异常 EvalException(java.lang.NullPointerException)
  class C { int m() { return 1; } }    → kind=TYPE_DECL          status=VALID                  value=null
  new C().m()                          → kind=VAR                status=VALID                  value=1
  int a = 6;                           → kind=VAR                status=VALID                  value=6
  a * 7;                               → kind=VAR                status=VALID                  value=42
  jshell 里的变量与方法:[$1:int, $2:int, s:String, $3:int, $4:int, a:int, $5:int] [f()int, h()int]
  无限循环 while (true) 在 500 ms 后被 js.stop() 中断:状态 VALID,异常 无,耗时 509 ms
  中断之后会话还能用:eval("n > 0") = true
来自 jshell 的标准输出
  static int sx = 1;     → status=VALID,诊断:[]
  final int fx = 2;      → status=VALID,诊断:[]
  private int px = 3;    → status=VALID,诊断:[]
  public int qx = 4;     → status=VALID,诊断:[]

分步演示

JShell
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12 的 jshell 与 jdk.jshell API
    • 没有验证:/edit 与外部编辑器、--startup 脚本、自定义反馈模式(/set feedback)、远程执行引擎的隔离性、与 Jupyter 等集成、JDK 21 之外版本的行为差异、/open 加载脚本文件;命令行输出里 jshell 提示符 jshell> 是真实输出,没有裁剪
    京ICP备2026067502号-1京公网安备11010502063487号