In this post I provide a tutorial on using awesome jshell tool.

1. Motivation

There are many times, when you just want to try out some code snippet in Java. For example, you want to experiment with new constructs available in recent Java versions, something like var.

For a long time, to do this you had to follow Write-Compile-Execute Loop:

  • write the Java program (of course with public static void main(String…​ args))

  • compile written code with javac (and potentially fix compile-time errors)

  • execute compiled byte-code with java (and potentially fix run-time exceptions)

  • edit written program and repeat the process

The frustrating thing here is delay between the time you start writing code and the time you actually got the feedback from Java compiler/runtime. However, now we have a tool, that finally removes unnecessary hustle. Please, meet jshell!

jshell significantly shortens the feedback loop — thus increasing your productivity. The boost is achieved by eliminating the need to switch back and forth between your editor/IDE and shell, since you write Java code in the dedicated shell (hence the name, jshell). More precisely, jshell implements Read-Evaluate-Print Loop:

  • reads the code from command line

  • evaluates the given snippet

  • prints the result back to you

If you think about it — this is pretty awesome!

Hopefully at this point you’re eager to play with jshell, so, without further ado — let’s start.

2. Starting / exiting jshell

  • Using JDK 9+

jshell is available on JDK 9+ and above. So, if you have one of the recent Java versions available in the $PATH, then all you need to do is simply launch jshell:

$ jshell -v
|  Welcome to JShell -- Version 11.0.4
|  For an introduction type: /help intro

jshell> /exit
|  Goodbye
-v flag — enables verbose feedback mode, that gives you a log of comments from jshell.
  • Using Docker

If you want to experiment with most recent features of Java in clean and safe playground — then just use Docker:

$ docker run --rm -it adoptopenjdk/openjdk13 jshell -v
Nov 03, 2019 12:08:49 AM java.util.prefs.FileSystemPreferences$1 run
INFO: Created user preferences directory.
|  Welcome to JShell -- Version 13
|  For an introduction type: /help intro

jshell> /exit
|  Goodbye
/exit — quites jshell.

3. Snippets

As already mentioned, the jshell tool allows you to execute Java code, getting immediate results. You can enter:

  • Java definition (variable, method, class, etc), like: int x = 8

  • Java expression, like: x + x

  • Java statement or import, like: import java.time.*

These little chunks of Java code are called snippets.

Let’s try write a few snippets to get familiar:

jshell> 1 + 1
$1 ==> 2
|  created scratch variable $1 : int

jshell> int n = 1 + 1
n ==> 2
|  created variable n : int

jshell> String hello(String g) {
   ...>   return "hello " + g;
   ...> }
|  created method hello(String)

jshell> String hello(String name) {
   ...>   return "Hi " + name + "!";
   ...> }
|  modified method hello(String)
|    update overwrote method hello(String) (1)

jshell> hello("Alina")
$5 ==> "Hi Alina!"
|  created scratch variable $5 : String

jshell> String n
n ==> null
|  replaced variable n : String
|    update overwrote variable n : int (2)

jshell> double volume(double radius) {
   ...>   return 4.0 / 3.0 * PI * cube(radius); (3)
   ...> }
|  created method volume(double), however, it cannot be invoked until variable PI, and method cube(double) are declared

jshell> double PI = 3.14159

jshell> volume(1)
|  attempted to call method volume(double) which cannot be invoked until method cube(double) is declared

jshell> double cube(double a) { return a * a * a; }

jshell> volume(1)
$11 ==> 4.188786666666666

jshell> int divide(int a, int b) {
   ...>   return a / b;
   ...> }

jshell> divide(1, 0)
|  Exception java.lang.ArithmeticException: / by zero
|        at divide (#12:2) (4)
|        at (#13:1)

jshell> /list (5)

   1 : 1 + 1
   4 : String hello(String name) {
         return "Hi " + name + "!";
       }
   5 : hello("Alina")
   6 : String n;
   7 : double volume(double radius) {
         return 4.0 / 3.0 * PI * cube(radius);
       }
   8 : double PI = 3.14159;
   9 : volume(1)
  10 : double cube(double a) { return a * a * a; }
  11 : volume(1)
  12 : int divide(int a, int b) {
         return a / b;
       }
  13 : divide(1, 0)
1overwrite existing variable/method/class definition
2overwrite existing variable/method/class definition using incompatible type
3forward reference variables/methods/classes, that are not yet defined
4#id:line-number snippet id and line number withing a snippet, that caused an exception
5/list command to trace back the source of exception

Verbose mode gives a lot of commentary, that might be useful, when you just start learning about jshell. When you gained enough experience, just change the feedback level:

/set feedback — displays the current mode and available modes

/set feedback concise — for terse commentary

/set feedback normal — for normal commentary

/set feedback silent — disables all feedback except errors

jshell supports auto-completion with <Tab> key:

jshell> "hello". [Tab] (1)
charAt(                chars()                codePointAt(
codePointBefore(       codePointCount(        codePoints()
compareTo(             compareToIgnoreCase(   concat(
contains(              contentEquals(         describeConstable()
endsWith(              equals(                equalsIgnoreCase(
formatted(             getBytes(              getChars(
getClass()             hashCode()             indent(
indexOf(               intern()               isBlank()
isEmpty()              lastIndexOf(           length()
lines()                matches(               notify()
notifyAll()            offsetByCodePoints(    regionMatches(
repeat(                replace(               replaceAll(
replaceFirst(          resolveConstantDesc(   split(
startsWith(            strip()                stripIndent()
stripLeading()         stripTrailing()        subSequence(
substring(             toCharArray()          toLowerCase(
toString()             toUpperCase(           transform(
translateEscapes()     trim()                 wait(

jshell> "hello".startsWith( [Tab+Tab] (2)

jshell> "hello".startsWith("h") [Shift+Tab v] (3)

jshell> boolean _ = "hello".startsWith("h") (4)
1press Tab key to auto-complete
2press Tab two times to show text documentation
3press Shift+Tab+v to declare the variable with already entered value
4after pressing Shift+Tab+v, the cursor is placed in the line, where you need to enter variable name

/help shortcuts — displays information about available shortcuts

Shift+Tab then v — the expression will be converted to a variable declaration

Shift+Tab then m — the expression or statement will be converted to a method declaration

Shift+Tab then i — propose possible imports

4. Commands

jshell commands control the environment and display information.

Command are distinguished from snippets by a leading forward slash /.

Probably, the most useful is /help command — it shows a list of available commands. Also, it allows to dig dipper into specific command. For example:

jshell> /help /var
|
|                                   /vars
|                                   =====
|
|  List the type, name, and value of variables that were entered.
|
|  /vars
|  	List the type, name, and value of the current active variables
|
|  /vars <name>
|  	List variables with the specified name (preference for active variables)
|
|  /vars <id>
|  	List the variable with the specified snippet ID.
|  	One or more IDs or ID ranges may used, see '/help id'
|
|  /vars -start
|  	List the variables in the evaluated startup snippets
|
|  /vars -all
|  	List all variables including failed, overwritten, dropped, and startup

jshell has a default startup script that is silently and automatically executed before start, so that you can get to work quickly. Entries from the startup script aren’t listed unless you request them with the /list -start or /list -all command:

jshell> /list -all

  s1 : import java.io.*;
  s2 : import java.math.*;
  s3 : import java.net.*;
  s4 : import java.nio.file.*;
  s5 : import java.util.*;
  s6 : import java.util.concurrent.*;
  s7 : import java.util.function.*;
  s8 : import java.util.prefs.*;
  s9 : import java.util.regex.*;
 s10 : import java.util.stream.*;

The default startup script consists of several common imports.

/set start command, can be used to personalize your startup entries (for more details, use /help /set start).

/save -start command saves personalized startup script.

Similar to snippet completion, when you enter commands and command options, use the Tab key to automatically complete the command or option.

jshell> /
/!          /?          /drop       /edit       /env        /exit
/help       /history    /imports    /list       /methods    /open
/reload     /reset      /save       /set        /types      /vars

<press tab again to see synopsis>

Yet another useful way to get help, is to press Tab second time, to see synopsis for specific command:

jshell> /env -
view or change the evaluation context

<press tab again to see full documentation>

Using Tab in a file argument position of the command shows the available files:

jshell> /open
.dockerenv   /            bin/         boot/        dev/         etc/
home/        lib/         lib64/       media/       mnt/         opt/
proc/        root/        run/         sbin/        srv/         sys/
tmp/         usr/         var/

<press tab again to see synopsis>

Command (and command option) abbreviations are supported, as long as the abbreviation is unique.

For example, the only command that begins with /l is /list, and the only /list option that begins with -a is -all. Therefore, you can use the following abbreviations to enter the /list -all command:

jshell> /l -a

5. Search

By pressing up arrow / down arrow you can navigate through the history of entered items.

However, sometimes it’s pretty tedious to go through history one line at a time. So, to perform backward history search — use Ctrl+R:

jshell>
bck-i-search: _

Now, you can enter keyword to search for (e.g. class — if you want to search for all previously defined classes). You can continue go further back history by repeatedly pressing Ctrl+R. And if at some point you want to move the search forward towards the present — use Ctrl+S.

6. External editor

An alternative to editing at the command prompt is to use an external editor. This editor can be used to edit and create snippets, and is especially helpful for multiline snippets.

To edit a specific snippet in an editor, use /edit command with snippet name or id. Use /list command to get snippet id.

You can configure jshell to use the editor of your choice with the /set editor command:

jshell> /set editor vim
|  Editor set to: vim

jshell> /edit

Now, you can define multi-line snippet in external editor, and after saving and closing editor, jshell prompt is restored:

jshell>

If you don’t specify an editor, then the following environment variables are checked in order: JSHELLEDITOR, VISUAL, and EDITOR. If none of those are set, then a simple default editor is used.

7. Conclusion

jshell doesn’t replace IDEs. The tool is there for you to learn and experiment with Java code.

jshell provides very convenient and safe playground. So, you can try different ideas and, after getting satisfactory results, just copy final code into your program editor or IDE.

Overall, in my opinion, jshell significantly shortens the feedback loop — thus increasing your productivity. The boost is achieved by removing the need to switching back and forth between your editor/IDE and shell.

Oleksii Zghurskyi