Java has long been criticized for its verbosity and compile-run cycle that slows down experimentation and learning. This changed with Java 9, which introduced JShell—Java's first official REPL (Read-Eval-Print Loop) tool. JShell allows developers to interactively evaluate Java code snippets without the ceremony of class declarations and compilation cycles, revolutionizing how we learn, prototype, and debug in Java.
This article explores JShell, from basic usage to advanced features, showing how this powerful tool can enhance your Java workflow.
What is JShell?
JShell is an interactive command-line tool that provides immediate feedback on Java code execution. Unlike traditional Java development that requires creating classes with main methods and compiling files, JShell lets you type Java expressions and see results instantly.
Key Benefits:
- Instant Feedback: Execute code immediately without compilation
- Rapid Prototyping: Test ideas and algorithms quickly
- Learning Tool: Ideal for beginners learning Java syntax
- API Exploration: Experiment with libraries without setup overhead
- Debugging Aid: Isolate and test problematic code sections
Getting Started with JShell
Starting JShell:
# Basic startup jshell # Start with verbose feedback jshell -v # Start with pre-loaded imports jshell --start DEFAULT --start PRINTING
Basic JShell Session:
// Welcome to JShell -- Version 21 // For an introduction type: /help intro jshell> 2 + 2 $1 ==> 4 jshell> String greeting = "Hello, JShell!" greeting ==> "Hello, JShell!" jshell> System.out.println(greeting) Hello, JShell! jshell> int x = 10, y = 20 x ==> 10 y ==> 20 jshell> x + y $5 ==> 30
Key JShell Features
1. No Main Method Required
// Traditional Java requires this:
// public class Hello {
// public static void main(String[] args) {
// System.out.println("Hello World");
// }
// }
// In JShell, just type:
jshell> System.out.println("Hello World")
Hello World
2. Automatic Variable Capture
jshell> 5 * 8 $1 ==> 40 jshell> $1 + 10 $2 ==> 50 jshell> String result = "The answer is: " + $2 result ==> "The answer is: 50"
3. Method Definitions
jshell> int square(int n) {
...> return n * n;
...> }
| created method square(int)
jshell> square(5)
$3 ==> 25
jshell> square(12)
$4 ==> 144
4. Class Definitions
jshell> class Person {
...> String name;
...> int age;
...>
...> Person(String name, int age) {
...> this.name = name;
...> this.age = age;
...> }
...>
...> String getInfo() {
...> return name + " is " + age + " years old";
...> }
...> }
| created class Person
jshell> Person alice = new Person("Alice", 30)
alice ==> Person@5e91993f
jshell> alice.getInfo()
$6 ==> "Alice is 30 years old"
JShell Commands
JShell provides meta-commands (starting with /) to manage the environment:
Essential Commands:
// List all entered snippets jshell> /list // List variables jshell> /vars | int $1 = 4 | String greeting = "Hello, JShell!" | int x = 10 | int y = 20 | int $5 = 30 // List methods jshell> /methods | int square(int) // List classes jshell> /classes | class Person // View history jshell> /history // Save session to file jshell> /save mysession.jsh // Load session from file jshell> /open mysession.jsh // Reset session jshell> /reset // Exit JShell jshell> /exit
Advanced Commands:
// View all commands jshell> /help // Edit a snippet jshell> /edit square // View current imports jshell> /imports // Drop a variable/method/class jshell> /drop square // Reload with different startup configuration jshell> /reload
Practical JShell Use Cases
Use Case 1: Learning and Experimentation
jshell> // Explore String methods jshell> String text = "Hello World" text ==> "Hello World" jshell> text.length() $2 ==> 11 jshell> text.toUpperCase() $3 ==> "HELLO WORLD" jshell> text.substring(6) $4 ==> "World" jshell> text.chars().map(Character::toLowerCase).forEach(c -> System.out.print((char)c)) hello world
Use Case 2: API Exploration
jshell> import java.time.* jshell> import java.time.temporal.* jshell> LocalDate today = LocalDate.now() today ==> 2024-01-15 jshell> LocalDate birthday = LocalDate.of(1990, Month.JUNE, 15) birthday ==> 1990-06-15 jshell> Period age = Period.between(birthday, today) age ==> P33Y7M jshell> ChronoUnit.DAYS.between(birthday, today) $5 ==> 12287
Use Case 3: Algorithm Testing
jshell> // Test a sorting algorithm
jshell> int[] numbers = {5, 2, 8, 1, 9, 3}
numbers ==> int[6] { 5, 2, 8, 1, 9, 3 }
jshell> Arrays.sort(numbers)
jshell> Arrays.toString(numbers)
$7 ==> "[1, 2, 3, 5, 8, 9]"
jshell> // Test binary search
jshell> Arrays.binarySearch(numbers, 5)
$8 ==> 3
jshell> Arrays.binarySearch(numbers, 4)
$9 ==> -4
Use Case 4: Mathematical Calculations
jshell> import java.math.*
jshell> BigDecimal principal = new BigDecimal("1000.00")
principal ==> 1000.00
jshell> BigDecimal rate = new BigDecimal("0.05")
rate ==> 0.05
jshell> int years = 5
years ==> 5
jshell> BigDecimal futureValue = principal.multiply(rate.add(BigDecimal.ONE).pow(years))
futureValue ==> 1276.281562500000
jshell> futureValue.setScale(2, RoundingMode.HALF_UP)
$14 ==> 1276.28
Advanced JShell Features
1. Exception Handling
jshell> try {
...> int result = 10 / 0;
...> } catch (ArithmeticException e) {
...> System.out.println("Cannot divide by zero: " + e.getMessage());
...> }
Cannot divide by zero: / by zero
2. Lambda Expressions
jshell> import java.util.*
jshell> import java.util.stream.*
jshell> List<String> names = Arrays.asList("Alice", "Bob", "Charlie")
names ==> [Alice, Bob, Charlie]
jshell> names.stream()
...> .filter(name -> name.length() > 3)
...> .map(String::toUpperCase)
...> .forEach(System.out::println)
ALICE
CHARLIE
3. Working with Files
jshell> import java.nio.file.*
jshell> Path tempFile = Files.createTempFile("jshell", ".txt")
tempFile ==> /tmp/jshell123456789.txt
jshell> Files.writeString(tempFile, "Hello from JShell!")
$19 ==> 18
jshell> Files.readString(tempFile)
$20 ==> "Hello from JShell!"
4. Custom Imports and Startup Scripts
Create a startup file startup.jsh:
// Commonly used imports
import java.util.*
import java.util.stream.*
import java.time.*
import java.math.*
import java.nio.file.*
// Utility methods
void printArray(Object[] array) {
System.out.println(Arrays.toString(array));
}
String repeat(String s, int n) {
return s.repeat(n);
}
Load it:
jshell startup.jsh
JShell Configuration and Customization
Creating Custom Startup:
# Create custom startup file
echo "import java.time.*" > my-startup.jsh
echo "void println(Object obj) { System.out.println(obj); }" >> my-startup.jsh
# Start with custom startup
jshell --startup my-startup.jsh
Using JShell with IDEs:
- IntelliJ IDEA: Built-in JShell console
- Eclipse: Available through plugins
- VS Code: Java Extension Pack includes JShell support
Feedback Modes:
# Different feedback levels jshell --feedback verbose jshell --feedback concise jshell --feedback silent
Real-World Development Workflow
Problem Solving Approach:
// Step 1: Define the problem
jshell> // Need to calculate factorial
// Step 2: Test small cases
jshell> int factorial(int n) {
...> if (n <= 1) return 1;
...> return n * factorial(n - 1);
...> }
| created method factorial(int)
// Step 3: Verify with known values
jshell> factorial(5)
$1 ==> 120
jshell> factorial(10)
$2 ==> 3628800
// Step 4: Handle edge cases
jshell> factorial(0)
$3 ==> 1
jshell> factorial(1)
$4 ==> 1
API Design Testing:
jshell> // Design a simple Calculator API
jshell> class Calculator {
...> static double add(double a, double b) { return a + b; }
...> static double subtract(double a, double b) { return a - b; }
...> static double multiply(double a, double b) { return a * b; }
...> static double divide(double a, double b) {
...> if (b == 0) throw new ArithmeticException("Division by zero");
...> return a / b;
...> }
...> }
| created class Calculator
jshell> Calculator.add(10, 5)
$5 ==> 15.0
jshell> Calculator.divide(10, 0)
| Exception java.lang.ArithmeticException: Division by zero
| at divide (#6:4)
| at (#7:1)
Best Practices for JShell
- Use for Prototyping: Ideal for testing ideas before implementing in main codebase
- Keep Sessions Focused: Reset often to avoid clutter
- Save Useful Sessions: Use
/savefor valuable experiments - Leverage Auto-completion: Use Tab completion for method discovery
- Combine with Documentation: Use JShell alongside Java API documentation
// Good practice: Test small, reset often jshell> /reset | Resetting state. jshell> // New focused session for specific problem jshell> // Testing collection operations...
Limitations and Considerations
What JShell Cannot Do:
- Access control modifiers have limited meaning
- No package declarations
- Some complex Java features may not work as expected
- Not suitable for production code
When to Use Traditional Development:
- Building complete applications
- Team development with version control
- Production-ready code with proper architecture
- Complex multi-class systems
Conclusion
JShell represents a significant evolution in the Java ecosystem, providing:
- Immediate Feedback Loop: Test code instantly without compilation cycles
- Enhanced Learning Experience: Perfect for students and developers learning Java
- Rapid Prototyping: Quickly validate ideas and algorithms
- API Exploration: Experiment with libraries in an interactive environment
- Debugging Support: Isolate and test problematic code sections
By integrating JShell into your development workflow, you can significantly reduce the feedback cycle, accelerate learning, and improve code quality through thorough experimentation and testing. Whether you're a beginner learning Java or an experienced developer exploring new APIs, JShell is an invaluable tool that makes Java development more interactive and enjoyable.