Author avatar

Esteban Herrera

Getting Started with JShell Part 2

Esteban Herrera

  • Dec 14, 2018
  • 9 Min read
  • 8,819 Views
  • Dec 14, 2018
  • 9 Min read
  • 8,819 Views
Java and J2EE
JShell

Preface

In the first guide of this series, you learned what JShell is and how to install it.

In this guide, you'll learn how to start working with Jshell.

Expressions and Variables

So, let's start with a simple expression:

1
2
jshell> "Hello World"
$1 ==> "Hello World"
java

Notice that the result of the evaluation is shown and it's assigned to the variable named $1. You can check this by entering the name of the variable:

1
2
jshell> $1
$1 ==> "Hello World"
java

A variable like this will be created implicitly when the result of an expression is not assigned to some variable. If the expression doesn't have a return value (like a print statement), no variable will be created.

You can use this implicit variable like any other:

1
2
jshell> System.out.println($1)
Hello World
java

You can also change the value it holds:

1
2
3
4
5
jshell> $1 = $1 + " again"
$1 ==> "Hello World again"

jshell> System.out.println($1)
Hello World again
java

However, it's probably better to work with some meaningful identifiers:

1
2
3
4
5
jshell> String hello = $1
hello ==> "Hello World again"

jshell> System.out.println(hello)
Hello World again
java

As you can see in the previous examples, it's not required to use a semicolon for single statements. Of course, this will not work if you try to execute more than one statement at the same time:

1
2
3
4
5
jshell> int sum = 1 + 2 System.out.println(sum)
|  Error:
|  ';' expected
|  int sum = 1 + 2 System.out.println(sum);
|                 ^
java

You'll need a semicolon between statements (but not after the last one, if you don't want):

1
2
3
jshell> int sum = 1 + 2; System.out.println(sum)
sum ==> 3
3
java

Also, JShell won't execute a statement until it knows the statement is completed. For example, if you only type if(sum > 0), the prompt will change to ...>:

1
2
jshell> if(sum >0)
   ...> 
java

We'll be prompted to continue entering snippets until a valid statement or block is completed:

1
2
3
jshell> if(sum > 0)
   ...> System.out.print(sum)
3
java

Methods

The capability of typing multi-line statements allows us to write complete methods and classes. For example, you can define a method to return the negative value of the argument:

1
2
3
4
jshell> int getNegativeValue(int number) {
   ...> return -number;
   ...> }
|  created method getNegativeValue(int)
java

This way, you can call it like this:

1
2
jshell> getNegativeValue(10)
$5 ==> -10
java

Referencing

Sometimes, a method references variables or other methods that are defined later in a class. These are called forward references. Since in JShell the code is entered and evaluated sequentially, code that uses forward references cannot be invoked until these references are evaluated. Take the following method declaration for example:

1
2
3
4
jshell> int addMagicNumber(int number) {
   ...> return number + MAGIC_NUMBER;
   ...> }
|  created method addMagicNumber(int), however, it cannot be invoked until variable MAGIC_NUMBER is declared
java

You won't be able to use it until MAGIC_NUMBER is defined:

1
2
3
4
5
6
7
8
jshell> System.out.println(addMagicNumber(3))
|  attempted to call method addMagicNumber(int) which cannot be invoked until variable MAGIC_NUMBER is declared

jshell> int MAGIC_NUMBER = 1
MAGIC_NUMBER ==> 1

jshell> System.out.println(addMagicNumber(3))
4
java

JShell supports forward references in:

  • Methods
  • The type of
    • Return statements
    • Parameters
    • Variables

However, it doesn't support forward references in variable initializers (possibly because they need to be executed at the moment the variable is defined):

1
2
3
4
5
6
jshell> int MAGIC_NUMBER = getMagicNumber()
|  Error:
|  cannot find symbol
|    symbol:   method getMagicNumber()
|  int MAGIC_NUMBER = getMagicNumber();
|                     ^------------^
java

Classes

You can also declare classes, for example:

1
2
3
4
5
6
jshell> class Book {
   ...> private String title;
   ...> public void setTitle(String title) { this.title = title; }
   ...> public String getTitle() { return title; }
   ...> }
|  created class Book
java

And use them like this:

1
2
3
4
5
6
7
jshell> Book book = new Book()
book ==> Book@3dd4520b

jshell> book.setTitle("Java 9");

jshell> System.out.println(book.getTitle())
Java 9
java

Modifiers of methods and fields defined inside a class are applied just like in Java:

1
2
3
4
5
jshell> book.title
|  Error:
|  title has private access in Book
|  book.title
|  ^--------^
java

External Declarations

Declarations outside a class or interface (and declarations of classes and interfaces by themselves) are created under the following rules:

  1. Access modifiers (public, protected, and private) are ignored. All declaration snippets are accessible to all other snippets:

    1
    2
    3
    4
    5
    jshell> private int x = 1;
    x ==> 1
    
    jshell> System.out.print(x) // private ignored
    1
    java
  2. The modifier final is ignored. Changes and inheritance are allowed:

    1
    2
    3
    4
    5
    6
    jshell> final class A {void m() {} }
    |  Warning:
    |  Modifier 'final'  not permitted in top-level declarations, ignored
    |  final class A { void m() {} }
    |  ^---^
    |  created class A
    java
  3. The modifier static is ignored because there isn't a container class (at least none you can use):
    1
    2
    3
    4
    5
    6
    jshell> static char letter = 'A'
    |  Warning:
    |  Modifier 'static'  not permitted in top-level declarations, ignored
    |  static char letter = 'A';
    |  ^----^
    letter ==> 'A'
    java
  4. The modifiers default and synchronized are not allowed:
    1
    2
    3
    4
    5
    jshell> synchronized void method() {}
    |  Error:
    |  Modifier 'synchronized'  not permitted in top-level declarations
    |  synchronized void method() {}
    |  ^----------^
    java
  5. The modifier abstract is allowed only on classes:
    1
    2
    3
    4
    5
    jshell> abstract void method();
    |  Error:
    |  Modifier 'abstract'  not permitted in top-level declarations
    |  abstract void method();
    |  ^------^
    java

Exceptions

JShell automatically catches exceptions, display information about them, and allows you to continue the session as if nothing has happened. This works for unchecked exceptions:

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

jshell>
java

And checked exceptions, without wrapping them in a try/catch statement:

1
2
3
4
5
6
7
jshell> FileInputStream fis = new FileInputStream("nonexistentfile.txt")
|  java.io.FileNotFoundException thrown: nonexistentfile.txt (The system cannot find the file specified)
|        at FileInputStream.open0 (Native Method)
|        at FileInputStream.open (FileInputStream.java:196)
|        at FileInputStream.<init> (FileInputStream.java:139)
|        at FileInputStream.<init> (FileInputStream.java:94)
|        at (#25:1)
java

Finally, to end a session just type /exit:

1
2
3
4
jshell> /exit
|  Goodbye

$
java

Saving Files

One problem we have is that all these statements are lost when we exit JShell. Luckily, there's a /save command that can save all commands and snippets to a file. It has the following options:

  • /save <file>: Save the commands and snippets currently active to the provided file path.
  • /save -all <file>: Save all commands and snippets, including overwritten, start-up, and failed commands and snippets to the provided file path.
  • /save -history <file>: Save all commands and snippets run to the provided file.
  • /save -start <file>: Save all commands and snippets that initialized the JShell session to the provided path.

This way, if we execute:

1
/save my-session.txt
java

A text file named my-session.txt will be saved in the current directory with the statements you issued in the last session. For example:

1
2
3
4
5
6
7
8
9
"Hello World"
System.out.println($1)
"Hello World modified";
class Book {
private String title = "<NO TITLE>";
public void setTitle(String title) { this.title = title; }
public String getTitle() { return title; }
public String toString() { return "Book: " + title; }
}
java

This way, if we open a new JShell session and execute the command:

1
/open my-session.txt
java

All the statements defined in the text file will be executed:

1
2
jshell> /open my-session.txt
Hello World
java

By the way, /open also works with existing Java source files, for example:

1
jshell> /open Book.java
java

Next Steps

In the next guide in this series, you'll learn how to manage code snippets and use JShell to explore libraries.

3