Core Java

Java 9 Jshell Tutorial

1. Introduction

In this article we will learn about Java 9 Jshell. The Java Shell tool (JShell) is an interactive tool for learning the Java programming language and prototyping Java code. JShell is a Read-Evaluate-Print Loop (REPL), which evaluates declarations, statements, and expressions as they are entered and immediately shows the results. The tool is run from the command line. Using JShell, you can enter program elements one at a time, immediately see the result, and make adjustments as needed.  Java program development typically involves the following process: write a complete program, compile it and fix any errors, run the program, figure out what is wrong with it, edit it, repeat the process. JShell helps you try out code and easily explore options as you develop your program. You can test individual statements, try out different variations of a method, and experiment with unfamiliar APIs within the JShell session. JShell doesn’t replace an IDE. As you develop your program, paste code into JShell to try it out, and then paste working code from JShell into your program editor or IDE.

2. Starting and Stopping

JShell is included in JDK 9. To start JShell, enter the jshell command on the command line. JDK 9 must be installed on your system. If your path doesn’t include java-home/jdk-9/bin, start the tool from within that directory. To start JShell in verbose mode, use the -v option To exit JShell, enter /exit

jshell> /exit
| Goodbye

3. Snippet

JShell accepts Java statements; variable, method, and class definitions; imports; and expressions. These pieces of Java code are referred to as snippets. Snippets of Java code are entered into JShell and immediately evaluated. Feedback about the results, actions performed, and any errors that occurred is shown.

Enter the following sample statement at the prompt, and review the output that is shown:

jshell> double money = 35.8
money ==> 35.8
| created variable money : double

jshell>

First, the result is shown. Read this as: the variable money has the value 35.8. Because you are in verbose mode, a description of what occurred is also shown. Informative messages start with a vertical bar. Notice that both the name and the type of the created variable are shown. Note that the terminating semicolons are automatically added to the end of a complete snippet if not entered.
When an expression is entered that doesn’t have a named variable, a scratch variable is created so that the value can be referenced later. The following example shows scratch values for an expression and for the results of a method. The example also shows the continuation prompt (…>) that is used when a snippet requires more than one line of input to complete:

jshell> 304 - 8
$4 ==> 296
|  created scratch variable $4 : int

jshell> int square (int s) {
   ...> return s*s;
   ...> }
|  created method square(int)

jshell> square(25)
$6 ==> 625
|  created scratch variable $6 : int

jshell>

3.1 Modification

As you experiment with code, you might find that the definition of a variable, method, or class isn’t doing what you want it to do. The definition is easily changed by entering a new one, which overwrites the previous definition. To change the definition of a variable, method, or class, simply enter a new definition. For example, the square method that was defined above gets a new definition in the following example:

jshell> String square(int s) {
...> return "Square => " + s*s;
...> }
| replaced method square(int)
| update overwrote method square(int)

jshell> square(25)
$8 ==> "Square => 625"
| created scratch variable $8 : String

jshell>

Notice that instead of showing created method as before, the feedback shows replaced method. This message means that the definition changed, but the method has the same signature, and therefore all existing usages continue to be valid.

JShell was started in the verbose feedback mode, which provides a lot of commentary. You can set the amount and format of output with the /set feedback command, for example /set feedback concise. If you primarily use JShell by pasting from other windows, then you might prefer a feedback mode with no prompt and only error feedback. If so, then enter the /set feedback silent command.

3.2 Forward references

JShell accepts method definitions that reference methods, variables, or classes that aren’t yet defined. This is done to support exploratory programming and because some forms of programming require it. As an example, if you want to define a method for the volume of a sphere, then you can enter the following formula as the method volume:

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

jshell>

JShell allows the definition but warns of what is yet to be defined. The definition can be referenced, but if execution is attempted, then it fails until all of the required elements are defined:

jshell> double PI = 3.14
PI ==> 3.14
| created variable PI : double

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

jshell> double cube(double q) {
...> return q * q * q;
...> }
| created method cube(double)
| update modified method sphereVolume(double)

jshell> sphereVolume(5)
$13 ==> 475.75757575757575
|   created scratch variable $13 : double

To change the precision of PI, enter the new value as shown in the following example:

jshell> BigDecimal PI = new BigDecimal("3.14")
PI ==> 3.14
|   replaced variable PI : BigDecimal
|     update modified method sphereVolume(double) which cannot be invoked until this error is corrected:
|       bad operand types for binary operator '*'
|         first type: double
|         second type: java.math.BigDecimal
|       return 4.0/3.3 * PI * cube(radius);
|              ^----------^
|     update overwrote variable PI : double

The new definition of PI is type-incompatible with the definition of sphereVolume(). Because you are in verbose mode, update information is shown for other definitions affected by the change, which in this case describes the incompatibility. Notice that verbose mode is the only predefined feedback mode that displays update information. In other feedback modes, no warning is displayed until the code is executed. The purpose of this is to prevent an overload of updates. In all predefined modes, executing the sphereVolume() method displays the issue.

3.3 Exceptions

In an exception backtrace, feedback identifies the snippet and the location within the snippet where the exception occurred. The location within the code entered into JShell is displayed as #ID:line-number, where snippet ID is the number displayed by the /list command, and line-number is the line number within the snippet. In the following example, the exception occurs in snippet 1, which is the divide() method, on the second line of the method:

jshell> int divide(int x, int y) {
  ...> return x / y;
  ...> }
| created method divide(int,int)

jshell> divide(3,0)
|   java.lang.ArithmeticException thrown: / by zero
|     at divide (#1:2)
|     at (#2:1)

jshell> /list

  1 : int divide(int x, int y) {
      return x / y;
      }
  2 : divide(3,0)

3.4 Tab completion

When you enter snippets, use the Tab key to automatically complete the item. If the item can’t be determined from what was entered, then possible options are provided.

jshell> divide(<tab>
$2 divide(

Signatures:
int divide(int x, int y)

<press tab again to see documentation>

jshell> divide(
int divide(int x, int y)
<no documentation found>

<press tab again to see all possible completions; total possible completions: 541>

If the item can be completed in more than one way, the set of possibilities is displayed:

jshell> System.c
class clearProperty( console() currentTimeMillis()

Any common characters are added to what you entered, and the cursor is placed at the end of the input so that more can be entered. When you are at a method call’s open parenthesis, pressing Tab shows completion possibilities with the parameter types:

jshell> "Java Code Geeks".replace(
Signatures:
String String.replace(char oldChar, char newChar)
String String.replace(CharSequence target, CharSequence replacement)

<press tab again to see documentation>

Pressing Tab again shows a plain-text version of the documentation for the first method.

3.5 Snippet transformation

JShell makes it easy to import a needed class when it is first referenced and convert an expression to a variable declaration using keyboard shortcuts. When you enter an identifier that isn’t imported yet, press Shift+Tab i immediately after the identifier to see the options that enable you to add the import to your session. Enter the number of the option you want. More than one import option could be provided.

jshell> new JFrame<Shift+Tab i>
0: Do nothing
1: import: javax.swing.JFrame
Choice: 1
Imported: javax.swing.JFrame

You can convert an expression to a variable declaration by pressing Shift+Tab v after you enter the expression. The expression becomes the initial value of the variable declaration, and the type of the expression becomes the type of the variable. After you press Shift+Tab v, the cursor, which is indicated by a vertical bar (|) in the example, is placed in the line where you need to enter the variable name:

jshell> new JFrame("Demo") <Shift+Tab v>
jshell> JFrame | = new JFrame("Demo")

The expression must be valid or the transformation request is ignored.Sometimes the result type of the expression isn’t imported yet. In that case, Shift+Tab v offers to both import and create the variable.

4. Commands

JShell commands control the environment and display information within a session. Commands are distinguished from snippets by a leading forward slash (/). For information about the current variables, methods, and types, use the /vars, /methods, and /types commands. For a list of entered snippets, use the /list command.

jshell> /vars
|   int $2 = 0

jshell> /methods
|   int divide(int,int)

jshell> /types

jshell> /list

  1 : int divide(int x, int y) {
      return x / y;
      }
  2 : divide(3,0)

Notice that the types and values of variables and the type signature of methods are displayed. JShell has a default startup script that is silently and automatically executed before JShell starts, 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.*;
   1 : int divide(int x, int y) {
       return x / y;
       }
   2 : divide(3,0)

The default startup script consists of several common imports. You can personalize your startup entries with the /set start command. For information about this command, enter /help /set start. The /save -start command saves the current startup script as a starting point for your own startup script. Other important commands include /exit to leave JShell, /save to save your snippets, and /open to enter snippets from a file.

4.1 Tab completion

Similar to snippet completion, when you enter commands and command options, use the Tab key to automatically complete the command or option. If the completion can’t be determined from what was entered, then possible choices are provided. The following example shows the feedback when Tab is pressed after the leading slash (/) for commands:

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

<press tab again to see synopsis>

Unique completions are done in-place. For example, after you enter /h and press Tab, the line is replaced with /history:

jshell> /h
/help       /history

Tab completion also works for command options. The following example shows the use of the Tab key to display the options for the /vars command:

jshell> /vars -
-all      -start

Notice the message about pressing Tab again to show the command synopsis, which is a short description of the command. Press Tab a third time to show the help documentation. The following example shows the results of pressing Tab a second and third time:

jshell> /list
$2         -all       -history   -start     1          2          divide     s1         s10        s2         s3         s4         s5         s6         s7         s8         s9

jshell> /list
list the source you have typed

jshell> /list
Show the source of snippets, prefaced with the snippet id.

/list
    List the currently active snippets of code that you typed or read with /open

/list -start
    List the automatically evaluated start-up snippets

/list -all
    List all snippets including failed, overwritten, dropped, and start-up

/list 
    List snippets with the specified name (preference for active snippets)

/list 
    List the snippet with the specified snippet id

Completion of unique arguments is done in place. For example, after you enter /list -a, the -all option is automatically shown.
Snippet names can also be completed with Tab. For example, if you defined the divide method earlier in the JShell session, then pressing Tab after you start to enter the method name results in the full method name being displayed:

jshell> /edit d<tab>
jshell> /edit divide

4.2 Command Abbreviations

Reduce the amount of typing you have to do by using abbreviations. Commands, /set subcommands, command arguments, and command options can all be abbreviated, as long as the abbreviation is unique.

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

Also, the only command that begins with /se is /set, the only /set subcommand that begins with fe is feedback, and the only feedback mode that begins with v is verbose, assuming no custom feedback modes that start with v exist. Therefore, you can use the following abbreviations to set the feedback mode to verbose:

jshell> /se fe v

Notice that /s isn’t a sufficient abbreviation because /save and /set both begin with the same letter. When in doubt, you can use Tab completion to see the options.

5 Editing

JShell supports editing input at the jshell prompt and editing in an external editor of your choice. Shell editing enables you to edit snippets and commands as you enter them, and to retrieve and change previously entered snippets and commands. An external editor provides an alternate way to edit and create snippets, which is easier when you work with multiline snippets. Editing input at the command prompt makes it easy to correct your input and to retrieve and modify previously entered commands and snippets. Shell editing in JShell is built on JLine2, which is functionally similar to BSD editline and GNU readline in Emacs mode.

Shell editing is supported for editing the current line, or accessing the history through previous sessions of JShell. For navigating the input line, the Ctrl key and Meta key are used in key combinations. If your keyboard doesn’t have a Meta key, then the Alt key is often mapped to provide Meta key functionality.

For basic navigation within a line, use the right and left arrow keys or Ctrl+B for backwards and Ctrl+F for forward. For navigation between lines in the history, use the up and down arrow keys. Pressing the up arrow once replaces the current line with the previous command or snippet line. Pressing the up arrow again brings you to the line previous to that. The history contains both commands and snippet lines. If a snippet has multiple lines, then the up and down arrows navigate through each line of a snippet. The following table identifies the keys used and the actions taken to navigate the input line.

KeysAction
ReturnEnters the current line
Left arrowMoves backward one character
Right arrowMoves forward one character
Up arrowMoves up one line, backward through history
Down arrowMoves down one line, forward through history
Ctrl+AMoves to the beginning of the line
Ctrl+EMoves to the end of the line
Meta+BMoves backward one word
Meta+FMoves forward one word

5.1 History navigation

A history of snippets and commands is maintained across JShell sessions. This history provides you with access to items that you entered in the current and previous sessions.

To reenter or edit prior input, navigate the history using the up, down, left, and right arrows. Entered text is inserted at the cursor. The Delete key is used to delete text. Press the Enter key to reenter the history line, modified or not.

The up and down arrow keys move backward and forward through the history one line at a time.

Input lines retrieved from the history can be modified as needed and reentered, which saves you from having to retype a line just to make small changes. Add text at the current cursor position simply by entering it.

5.2 Searching

Searching the history is a feature of JShell that makes it easier to find the line you want without going through the history one line at a time. To start your search, press Ctrl-R. At the prompt, enter the search string. The search proceeds backward from your most-recent entry and includes previous sessions of JShell. The following example shows the prompt that is presented after pressing Ctrl-R:

jshell> <Ctrl+R>
((reverse-i-search)`':

Based on the example above, entering class changes the display to show the most-recent line with the text class:

(reverse-i-search)`class': class MyClass {

The search is incremental, so this line is retrieved with just the first character c. You can continue to search earlier in the history by pressing Ctrl+R repeatedly. Ctrl+S moves the search forward towards the present.
You can define a keyboard macro by entering Ctrl-x ( , then entering your text, and finally entering Ctrl-x ). To use your macro, enter Ctrl+x e

5.3 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. You can configure JShell to use the editor of your choice. To edit all existing snippets at once in an editor, use /edit without an option. To edit a specific snippet in an editor, use the /edit command with the snippet name or ID. Use the /list command to get the snippet IDs. You can also enter new snippets in the editor. When you save in the editor, any snippet that is changed or new is entered into the JShell session. Feedback from the snippets is shown in the JShell window, however, no JShell prompt is shown. You can’t enter commands or snippets in the JShell window until the editor is closed.

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. To set up JShell to open the editor of your choice, use the /set editor command. The argument to the /set editor command is the command needed to start the external editor that you want to use.

6. External code

External classes are accessed from a JShell session through the class path. External modules are accessed through the module path, additional modules setting, and module exports setting. Set the class path on the command line as shown in the following example:

% jshell --class-path mySpecialClassPath

Point your class path to directories or JAR files that have the packages that you want to access. The code must be compiled into class files. Code in the default package, which is also known as the unnamed package, can’t be accessed from JShell. After you set the class path, these packages can be imported into your session:

jshell> import com.javacodegeeks.*

You can also use the /env command to set the class path, as shown in the following example:

jshell> /env --class-path mySpecialClassPath
|  Setting new options and restoring state.

The /env command resets the execution state, reloading any current snippets with the new class path setting or other environment setting entered with the command.

Modules are supported in JShell. The module path can be set, additional modules to resolve specified, and module exports given.
Module options can be provided in options to the /env command or on the command line as shown in the following example:

 % jshell --module-path mySpecialModulePath  --add-modules com.javacodegeeks.module

To see current environment settings, use /env without options.

7. Feedback modes

A feedback mode defines the prompts and feedback that are used in your interaction with JShell. Predefined modes are provided for your convenience. You can create custom modes as needed.

The predefined modes can’t be modified, but they can be used as the base of a custom mode. The predefined modes, in descending order of verbosity are verbose, normal, concise, and silent.
The following table shows the differences in the predefined modes.

ModeValue SnippetsDeclarationUpdatesCommandsPrompt
verbosename ==> value (and description)YesYesYes\njshell>
normalname ==> valueYesNoYes\njshell>
concisename ==> value (only expressions)NoNoNojshell>
silentNoNoNoNo->
  • The Mode column indicates the mode that is being described.
  • The Value Snippets column indicates what is shown for snippets that have values, such as expressions, assignments, and variable declarations.
  • The Declaration column indicates if feedback is provided for declarations or methods, classes, enum, interfaces, and annotation interfaces.
  • The Updates column indicates if changes to other than the current snippet are shown.
  • The Commands column indicates if commands give feedback indicating success.
  • The Prompt column indicates the prompt that is used.

The default feedback mode is normal. Change the feedback mode by setting a command-line option or using the /set feedback command as shown in the following example:

jshell> /set feedback verbose
| Feedback mode: verbose

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

jshell> /set feedback concise
jshell> 2+2
$2 ==> 4
jshell> /set feedback silent
-> 2+2

Notice that when the setting is normal or verbose, the command feedback shows you the setting, but concise and silent modes don’t.
To see the current and available feedback modes, use the /set feedback command without options. Notice that the current mode is shown as the command that set it:

jshell> /set feedback
| /set feedback verbose
|
| Available feedback modes:
|   concise
|   normal
|   silent
|   verbose

7.1 Define a feedback mode

Custom feedback modes enable you to define the prompts that you want to see and the feedback that you want to receive for the different elements that you enter into JShell.

A feedback mode has the following settings:

  • Prompts: Regular and continuation
  • Truncation: Maximum length of values displayed
  • Format: Format of the feedback provided

The predefined modes can’t be changed, but you can easily create a copy of an existing mode, as shown in the following example:

jshell> /set mode mySpecialMode silent -command
| Created new feedback mode: mySpecialMode

The new mode mySpecialMode is a copy of the silent mode. The -command option indicates that you want command feedback. If you don’t want commands to describe the action that occurred, then use -quiet instead of -command.

Set Prompt

As with all /set commands, use the /set prompt command without settings to show the current setting:

jshell> /set prompt
|  /set prompt normal "\njshell> " "   ...> "
|  /set prompt silent "-> " ">> "
|  /set prompt concise "jshell> " "   ...> "
|  /set prompt mySpecialMode "-> " ">> "
|  /set prompt verbose "\njshell> " "   ...> "

All settings have a duration of the current session; they are not reset by the /reset command. If you want the settings to be the default for future sessions, then use the -retain option to keep them.

Set Truncation

If the values are too long, then they are truncated when displayed. Use the /set truncation command to set the maximum length shown for a value. If no settings are entered with the command, then the current setting is displayed. The following example shows the settings that were inherited from the silent mode:

jshell> /set truncation mySpecialMode
|  /set truncation mySpecialMode 80
|  /set truncation mySpecialMode 1000 expression,varvalue

The conditions under which a truncation setting is in effect is determined by the optional selectors that are entered after the truncation length. Two types of selectors (called selector kinds in the online help) are defined:

  • A case selector indicates the type of snippet whose value is displayed.
  • An action selector describes what happened to the snippet.

Enter /help /set truncation for details about selectors. The setting shown in the previous example means that values are truncated to 80 characters unless the value is the value of an expression (the expression case selector) or the value of a variable, as explicitly requested by entering just the variable name (the varvalue case selector). The order is important; the last one entered is used. If the order were reversed, then all of the values would be truncated to 80 characters.

8 Scripts

A JShell script is a sequence of snippets and JShell commands in a file, one snippet or command per line. Scripts can be a local file, or one of the following predefined scripts:

Script NameScript Contents
DEFAULTIncludes commonly needed import declarations. This script is used if no other startup script is provided.
PRINTINGDefines JShell methods that redirect to the print, println, and printf methods in PrintStream.
JAVASEImports the core Java SE API defined by the java.se module, which causes a noticeable delay in starting JShell due to the number of packages.

8.1 Startup script

Startup scripts contain snippets and commands that are loaded when a JShell session is started. The default startup script contains common import statements. You can create custom scripts as needed.

Startup scripts are loaded each time the jshell tool is reset. Reset occurs during the initial startup and with the /reset, /reload, and /env commands. If you do not set the script, then, the default startup script, DEFAULT, is used. This default script defines commonly needed import declarations.

To set the startup script, use the /set start command:

jshell> /set start mySpecialStartup.jsh

jshell> /reset
|  Resetting state.

As with all /set commands, the duration of the setting is the current session unless the -retain option is used. Typically, the -retain option isn’t used when you test a startup script setting. When the desired setting is found, use the -retain option to preserve it:

jshell> /set start -retain

The startup script is then loaded the next time you start the jshell tool. Remember that the startup scripts are loaded into the current session only when the state is reset. The contents of the script is stored, not a reference to the script. The script is read only at the time the /set start command is run. However, predefined scripts are loaded by reference and can be updated with new releases of the JDK.

Startup scripts can also be specified with the --startup command-line flag:

% jshell --startup mystartup.jsh

The -retain flag is used to set these predefined scripts as the startup scripts for future sessions of the jshell tool. Use /set start without arguments to see the details of what is defined by these startup scripts.

To set more than one startup script on the command line, use the --startup flag for each script:

% jshell --startup DEFAULT --startup PRINTING

A script can be created externally in an editor, or generated from items entered in JShell Use one of the following commands to create a script from the entries in a JShell session:

jshell> /save mySpecialSnippets.jsh
jshell> /save -history mySpecialHistory.jsh
jshell> /save -start mySpecialStartup.jsh

The first command shown in the example saves the current active snippets to mySpecialSnippets.jsh. The second command shown saves the history of all of the snippets and commands, both valid and invalid, to mySpecialHistory.jsh. The last command shown saves the contents of the current startup script setting to mySpecialStartup.jsh. The file name provided can be any valid file path and name.

Scripts can be loaded from the command line when a JShell session is started:

% jshell mysnippets.jsh

Scripts can also be loaded within a JShell session by using the /open command:

jshell> /open PRINTING

9. Conclusion

In this article we learned about Java 9 Jshell. Many compiled languages include tools (sometimes called REPL) for statements interpretation. Using these tools you can test code snippets rapidly without creating project. Take Scala as an example. Compilation can sometimes take a long time, but using repl each statement is executed instantly! That’s great when you are getting started with the language. Each expression gives you returned value and it’s type – that’s very valuable information.

In java, instead, we have to create a test or main method which prints results and needs to be recompiled every time you make a change.

JShell solves this problem. JShell is a Java read-eval-print loop tool first introduced in the JDK 9. One of the reasons JShell was proposed for Java 9 is that a lot of applications use their own mechanisms to interactively evaluate expressions, and the de facto library to use a Java REPL was often BeanShell, which is a dormant project since 2003, and which made arbitrary changes to the Java language.

Mohammad Meraj Zia

Senior Java Developer
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button