JShell Basics
JShell is Java's interactive console, included in every JDK since Java 9. You type a Java expression, statement, or declaration, press Enter, and see the result immediately. There is no class to write, no main method, and no separate compile step. This kind of tool is called a REPL (Read–Evaluate–Print Loop), and it is the fastest way to test an idea, check how an API behaves, or learn a language feature.
Starting and Exiting
jshell
| Welcome to JShell -- Version 25.0.1
| For an introduction type: /help intro
jshell>
The jshell> prompt waits for input. To leave, type /exit or press Ctrl+D.
If the command is not found, the JDK's bin directory is not on your PATH (see the Install JDK lesson). JShell is part of the JDK; it is not included in JRE-only runtimes.
Evaluating Expressions
Type an expression and JShell prints its value:
jshell> 7 * 6
$1 ==> 42
jshell> "Java".toUpperCase()
$2 ==> "JAVA"
jshell> Math.sqrt(144)
$3 ==> 12.0
jshell> 10 / 4
$4 ==> 2
jshell> 10 / 4.0
$5 ==> 2.5
Each result is stored in an automatically created variable named $1, $2, and so on, which you can use later:
jshell> $1 + 8
$6 ==> 50
Semicolons are optional for a single expression or statement on a line. JShell adds them for you.
Declaring Variables
jshell> int count = 10
count ==> 10
jshell> String name = "Ada"
name ==> "Ada"
jshell> var prices = List.of(19.99, 5.49, 120.0)
prices ==> [19.99, 5.49, 120.0]
jshell> count * 3
$10 ==> 30
Variables persist for the whole session. Unlike in a normal program, you may redeclare a variable with a different type, which replaces the old one:
jshell> String count = "ten"
count ==> "ten"
This makes experimenting easy: there is no need to invent new names every time you change your mind.
Multi-Line Code
When a line is incomplete, JShell shows a continuation prompt ...> and waits:
jshell> for (int i = 1; i <= 3; i++) {
...> System.out.println("Line " + i);
...> }
Line 1
Line 2
Line 3
JShell knows the statement is complete when the braces balance.
Defining Methods
Methods can be declared at the top level, outside any class:
jshell> int square(int n) {
...> return n * n;
...> }
| created method square(int)
jshell> square(9)
$12 ==> 81
A method may refer to something that does not exist yet. JShell creates it but marks it as unusable until the missing piece is defined:
jshell> double withTax(double amount) {
...> return amount * (1 + TAX_RATE);
...> }
| created method withTax(double), however, it cannot be invoked until variable TAX_RATE is declared
jshell> double TAX_RATE = 0.18
TAX_RATE ==> 0.18
jshell> withTax(100)
$15 ==> 118.0
This forward reference support lets you build up code in whatever order ideas occur to you.
Redefining a method with the same signature replaces it, so you can fix a bug and re-run immediately.
Defining Classes, Records, and Enums
jshell> record Product(String name, double price) {}
| created record Product
jshell> var p = new Product("Keyboard", 49.99)
p ==> Product[name=Keyboard, price=49.99]
jshell> p.price() * 2
$18 ==> 99.98
jshell> enum Size { S, M, L }
| created enum Size
jshell> Size.valueOf("M").ordinal()
$20 ==> 1
Classes and interfaces work the same way, which makes JShell useful for sketching a design before moving it into a project.
Default Imports
JShell starts with a set of commonly used packages already imported, including:
java.io.* java.math.* java.net.*
java.nio.file.* java.util.* java.util.concurrent.*
java.util.function.* java.util.regex.* java.util.stream.*
That is why List.of(...) worked above without an import. Other packages need an explicit import:
jshell> import java.time.*
jshell> LocalDate.now().plusDays(30)
$22 ==> 2026-12-10
/imports lists everything currently imported.
Exceptions and Errors
JShell reports errors without ending the session:
jshell> int x = "hello"
| Error:
| incompatible types: java.lang.String cannot be converted to int
| int x = "hello";
| ^-----^
jshell> List.of(1, 2, 3).get(5)
| Exception java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 3
| at ImmutableCollections$ListN.get (ImmutableCollections.java:...)
| at (#24:1)
Compile errors show the exact position. Runtime exceptions show a stack trace, where #24:1 means snippet 24, line 1.
A convenience specific to JShell: top-level statements may call methods that throw checked exceptions without a try/catch. In a normal program, the following would require handling IOException:
jshell> Files.readString(Path.of("notes.txt")).lines().count()
$25 ==> 12
JShell Commands
Lines starting with / are commands for JShell itself, not Java code:
| Command | Effect |
|---|---|
/help | List all commands; /help intro for a short tutorial |
/vars | List declared variables with their types and values |
/methods | List declared methods |
/types | List declared classes, interfaces, enums, and records |
/imports | List active imports |
/list | Show the snippets entered so far, with their IDs |
/edit <name or id> | Open a snippet in an editor window to modify it |
/drop <name or id> | Remove a snippet |
/save file.jsh | Save the session's snippets to a file |
/open file.jsh | Load and run snippets from a file |
/reset | Clear everything and start fresh |
/history | Show everything typed this session |
/exit | Quit |
jshell> /vars
| int count = 10
| String name = "Ada"
| List<Double> prices = [19.99, 5.49, 120.0]
| double TAX_RATE = 0.18
jshell> /methods
| int square(int)
| double withTax(double)
Commands can be abbreviated to any unique prefix, such as /v for /vars.
Tab Completion and Documentation
Press Tab to complete names and see available methods:
jshell> "hello".to<Tab>
toCharArray() toLowerCase( toString() toUpperCase(
After an opening parenthesis, pressing Tab shows the method's signatures. Pressing Tab again shows its documentation:
jshell> "hello".substring(<Tab>
Signatures:
String String.substring(int beginIndex)
String String.substring(int beginIndex, int endIndex)
<press tab again to see documentation>
This makes JShell a quick reference for any class in the JDK.
Shift+Tab followed by v creates a variable from the expression you have typed; Shift+Tab followed by i offers to add an import for an unresolved type.
Feedback Modes
JShell's verbosity is adjustable:
jshell> /set feedback verbose // describes every action in detail
jshell> /set feedback concise // minimal output
jshell> /set feedback normal // the default
Verbose mode shows the inferred types of results, which helps when learning:
jshell> /set feedback verbose
jshell> 10 / 4.0
$27 ==> 2.5
| created scratch variable $27 : double
Using Libraries and Scripts
Add JARs to the class path when starting:
jshell --class-path lib/gson-2.11.0.jar
jshell> import com.google.gson.Gson
jshell> new Gson().toJson(Map.of("ok", true))
$2 ==> "{\"ok\":true}"
Within a session, /env --class-path <path> changes the class path.
A .jsh file holds snippets that can be run as a script:
// report.jsh
var scores = List.of(72, 88, 95, 61, 79);
System.out.println("Average: " + scores.stream().mapToInt(i -> i).average().orElse(0));
System.out.println("Highest: " + Collections.max(scores));
/exit
jshell report.jsh
Average: 79.0
Highest: 95
The final /exit makes JShell quit after running the script instead of staying open at the prompt.
When to Use JShell
| Use JShell for | Use a source file or project for |
|---|---|
| Checking how a method behaves on specific input | Programs you will run again or share |
| Testing a regular expression or format string | Code with several interacting classes |
| Learning a new API or language feature | Anything that needs tests or a build tool |
| Quick calculations and data exploration | Applications with dependencies managed by Maven/Gradle |
A productive habit is to keep JShell open while coding. When unsure exactly what String.split, LocalDate.plusMonths, or BigDecimal.setScale will return for an edge case, try it in JShell first and then write the code with confidence.
Practical Example: Exploring an API
Suppose you need to compute a subscription's renewal date and are unsure how month arithmetic handles the end of a month:
jshell> import java.time.*
jshell> LocalDate.of(2026, 1, 31).plusMonths(1)
$2 ==> 2026-02-28
jshell> LocalDate.of(2028, 1, 31).plusMonths(1)
$3 ==> 2028-02-29
jshell> LocalDate.of(2026, 1, 31).plusMonths(1).plusMonths(1)
$4 ==> 2026-03-28
jshell> LocalDate.of(2026, 1, 31).plusMonths(2)
$5 ==> 2026-03-31
Four lines reveal an important behavior: plusMonths clamps to the last valid day of the target month (February 28, or 29 in a leap year), and adding one month twice is not the same as adding two months at once, because the clamped day carries forward. A billing system that renews by repeatedly adding one month would drift from the 31st to the 28th permanently. Discovering this in JShell, before writing the billing code, costs seconds; discovering it from customer complaints costs far more.