Pluralsight Logo
Author avatar

Esteban Herrera

Author badge Author

Getting Started with JShell Part 2

Esteban Herrera

Author BadgeAuthor
  • Aug 14, 2018
  • 9 Min read
  • 7,727 Views
  • Aug 14, 2018
  • 9 Min read
  • 7,727 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"

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"

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

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

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

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);
|                 ^

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

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)
   ...> 

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

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)

This way, you can call it like this:

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

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

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

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();
|                     ^------------^

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

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

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
|  ^--------^

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
  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
  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'
  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() {}
    |  ^----------^
  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();
    |  ^------^

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>

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)

Finally, to end a session just type /exit:

1
2
3
4
jshell> /exit
|  Goodbye

$

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

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; }
}

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

1
/open my-session.txt

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

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

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

1
jshell> /open Book.java

Next Steps

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

0