6

DECIPHERING SECRET MESSAGES

Image

You’ve probably sent secret, encoded notes to your friends to try to hide the messages from your parents or teachers. In this chapter, we’ll be doing something similar as we create a Secret Messages app.

Our previous app, the Hi-Lo guessing game, was all about numbers—too high, too low, or just right. By contrast, our Secret Messages app will focus on text. You’ll learn to work with text strings and manipulate character values in Java to produce the encoded messages.

The Caesar Cipher

Our app will encode and decode secret messages using the Caesar cipher, an algorithm developed more than 2,000 years ago that uses letter substitutions to encode messages.

Images

Figure 6-1: A Caesar cipher disk, showing a key value of 13

The Caesar cipher is named for the  Roman emperor Julius Caesar (100–44 BCE). Historians say Caesar liked to encode his private messages, such as notes to his military generals, by “shifting” the letters of the alphabet. For example, the cipher disk shown in Figure 6-1 shifts the alphabet by 13 letters.

In the disk, the outer letters align with the inner letters they should be substituted with, so an A becomes an N, a B becomes an O, and so on. This is the cipher that created the following example:

Secret messages are so cool!
Frperg zrffntrf ner fb pbby!

The first line is the plaintext: the original, readable version of the message. The second line is the ciphertext, the encoded version of the same message.

Try decoding this message back to its original plaintext by reversing the substitution: find each inner letter and substitute it with the corresponding outer letter.

Not all Caesar ciphers are symmetric like this one. Symmetric means that the same process can be used for both encoding and decoding a message. For example, F becomes S when encoding by adding 13, and S becomes F when decoding by subtracting 13, and you can use the same disk and key—the number used to shift the letters—for both processes. The number to shift the letters by (in this case 13) is called a key because knowing it “unlocks” the cipher. The Secret Messages app we’ll build will allow us to use any key value.

Setting Up the Secret Messages App

We’ll build the Secret Messages app just like we did the Hi-Lo guessing game: first, we’ll create a command line version, then a GUI for the desktop, and, finally, an Android mobile app. The command line version will look fairly simple, as shown in Figure 6-2, but it will allow us to test the secret message algorithm quickly and easily.

Images

Figure 6-2: The command line version of the Secret Messages app

As you can see in Figure 6-2, the program asks the user to enter a message to encode or decode, followed by a secret key value. The program then responds with the encoded message. Note that when we start coding the app, the entire message will be encoded, including spaces and punctuation. In the final version, we’ll add some logic to encode only letters, as shown in Figure 6-2.

Creating the Secret Messages Project in Eclipse

Let’s begin by opening Eclipse. If you still have files open from previous projects, close those files now. To create a new Java Project, go to File New Java Project and name the project SecretMessages. Click Finish.

In the Package Explorer pane, expand the SecretMessages project folder to see the src folder. Right-click the src folder and go to New Class to create a new Java source code file, which you’ll also name SecretMessages. Select the checkbox to create a main() method, as shown in Figure 6-3.

Images

Figure 6-3: Create a new Java project, with a new class file called SecretMessages, and select the checkbox to create a main() method stub.

Click Finish, and you’ll see the SecretMessages.java file in the main window in Eclipse. Let’s get started coding the Secret Messages app!

Beginning to Code SecretMessages.java

At the top of the SecretMessages.java file, above the public class SecretMessages declaration, add the import statement for java.util.Scanner so we can ask the user for input:

import java.util.Scanner;
public class SecretMessages {
    public static void main(String[] args) {  
       Scanner scan = new Scanner(System.in);
       System.out.println("Enter a message to encode or decode:");
    }
}

Inside the main() method, at , we set up a Scanner object named scan. Then, at , we prompt the user to enter a message to encode or decode.

After the user prompt, we’ll create a String variable, called message, to accept the line of text the user enters:

      System.out.println("Enter a message to encode or decode:");
      String message = scan.nextLine();

Then, we get the user’s next full line of input, up to an ENTER or RETURN character, using the nextLine() method of the scan object, and we store it in the string message.

So far, the app can ask the user for a message and scan that message into a variable. Now we need to learn how to manipulate the characters inside a string to create an encoded version of the message. Save your file before continuing to the next section.

Messing with Strings

Up to this point, the Secret Messages app looks pretty similar to the Hi-Lo guessing game app. We set up an input scanner, prompt the user for some input, scan the console for the user’s response, and then capture the input in a variable. What’s going to make the Secret Messages app different is its ability to work with the characters inside the string.

The Caesar cipher command line app will take a lot of steps to complete, so we’ll build our app in iterations. That means that instead of writing out the whole app all at once and hoping the code works in the end, we’ll create our app bit by bit, or one iteration at a time, and test it as we go along so that we’ll have a functioning app at each point. Each iteration might not have all the features we need, but we’ll eventually get to the complete, full-featured app.

We’ll need to use text processing to manipulate the inputted string into an output string. To begin working with strings of text in Java, we’ll build a simple message reverser. In other words, we’ll take the message the user entered and then give it back to them with the letters in reverse order. So, for example, "Meet me at the arcade at 5pm" would become "mp5 ta edacra eht ta em teeM".

First, we create a variable called output for the reversed string and set it equal to an empty string:

      String message = scan.nextLine();
      String output = "";

We’ll need a loop to run through the characters in the message. We can choose a for loop for convenience, because we know the number of characters in the message. (Later, we’ll use the message.length() method to tell us how many characters the message contains.) A for loop declaration in Java needs to do three things: initialize a loop variable, test a condition to keep going, and update the loop variable before the next iteration. We place semicolons between each of the three parts to separate them. The syntax looks like this:

for ( initialization; condition; update ) { body }

For example, open JShell and enter this code to create a for loop:

jshell> for ( int x = 0; x < 10; x++ ) { System.out.println(x); }

This loop will print the numbers 0 through 9. The initialization sets the first looping variable x to 0. The condition tests to make sure x is less than 10 before proceeding. Finally, the update adds 1 to x after each pass through the loop. x++ uses a shortcut called the increment operator because it increments, or adds 1 to, x each time through the loop. x++ is equivalent to the statement x = x + 1. In this example, the for loop prints the value of x each time through the loop, repeating 10 times and stopping when x is no longer less than 10.

So, to reverse the characters in the message string, we might initialize a variable to the position number, or index, of the last character in the string and then loop through the characters from last to first (in reverse order), going backward through the string. In our example, this would look like:

      for ( int x = message.length()-1; x >= 0; x-- ) { }

The character positions in a string in Java are numbered from 0 (the index of the first character) to the length minus 1. The first character is at index 0, and the nth character in a string is at index (n − 1).

In Figure 6-4, the first 10 characters of our sample message are shown. The index of each character in the string is shown below the character. The first letter, M, is at index 0, the first e is at index 1, and so on. Notice that spaces count as characters—there are two spaces shown here, at indexes 4 and 7. The 10th character in the message is at index 9, the letter t. This continues all the way to the last character in the message, at index message.length()-1.

Images

Figure 6-4: The characters in a message labeled with their indexes

We want to start our reversed message at the end of the string, so we initialize x to the message.length()-1, the index of the last character in the message. The condition is x >= 0 because we want to keep going all the way down to the first character of the message, at index 0. Finally, the update is x-- because we’re stepping backward through the string. The opposite of x++, x-- uses the decrement operator, which decreases the value of x by 1 each time. Come back into Eclipse and begin writing the for loop just below the previous line of code:

       String output = "";
       for ( int x = message.length()-1; x >= 0; x-- ) {
       }

To get the character at a specific position in a string, we use the charAt() (“character at”) method and give it the index location of the character we want. To append or add a character to a string, we use the + operator. Putting those together, we can build the reverse version of the string message and store it in the string output:

      for ( int x = message.length()-1; x >= 0; x-- ) {
          output += message.charAt(x);
      }

The body of the for loop is a single line. We’re getting the character at the xth index of message and adding it to output. Remember to use open and close braces around the body of the for loop, because we’ll want to add more lines inside the loop as the app grows.

Now we just need to show the output message to the screen, which we can do with System.out.println(output). The full message-reversing iteration of the app is shown in Listing 6-1.

import java.util.Scanner;
public class SecretMessages {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        System.out.println("Enter a message to encode or decode:");
        String message = scan.nextLine();
        String output = "";
        for ( int x = message.length()-1; x >= 0; x-- ) {
            output += message.charAt(x);
        }
        System.out.println(output);
    }
}

Listing 6-1: The first iteration of the Secret Messages app reverses the characters in the user’s message.

You can run the program in Eclipse and test it with your own message, as shown in Figure 6-5. This isn’t our finished Secret Messages app, but it does make the message harder to read and easy to decode.

Images

Figure 6-5: Run the program and enter your own message to reverse in the console window at the bottom of the screen.

You can even decode a message by copying the encoded, reversed message and pasting it into the program, as shown in Figure 6-6. So you can type a message, encode it, and paste it into a message to a friend, and then they can decode it by pasting the encoded message into the same program. You’ve built your first secret message encoder!

Images

Figure 6-6: Copy and paste an encoded message into the running program to decode it.

This version of the app is simple enough that you can read the messages without running them through the program, so it’s not a very secure way to send messages to your friends. But you did learn how to loop through a string of text using a for loop, how to get the string’s length(), how to access a specific character at a given index or position in the string using charAt(), and how to add characters to the end of a string using the + operator. In the next section, you’ll learn how to change the values of the individual characters in the message to make it harder to read, but still easy for our program to decode.

Characters and Values in Java

Building a better secret message encoder requires the ability to work with character values in strings of text. For the Caesar cipher, we need to be able to shift those values—for example, changing an A to an N, and an N to an A. To do this, you need to understand how characters are stored in a computer.

In Java, individual characters, such as 'A', can be stored in their own data type: char. Notice that we use single quotation marks to contain character values. The charAt() method of a string returns a char value representing a single character. Java uses 16-bit Unicode characters. Unicode is an international character set containing thousands of characters and symbols from all over the world that are represented as numeric values. The char type is a way to store Unicode characters, such as 'A', 'ñ', and 'ç', by their numeric values.

We can add to a Unicode char value in the same way we can add to an int variable. With the Secret Messages app, we want to add the char value for 'A' (65) to the key (13) to get the new char value (78), which represents the encoded letter ('N').

For our second iteration of the app, let’s begin coding the Caesar cipher by setting up a char variable called key and storing the value 13 in it. We’ll add this below the String output line:

      String output = "";
      char key = 13;

We also need to modify the for loop to go from the beginning of the string to the end. We’re starting at index 0 this time and moving through the string while x is less than message.length(). Each time we repeat the loop, we’re adding 1 to x, the character position. Here is the new for loop:

      char key = 13;
      for ( int x = 0; x < message.length(); x++ ) {

Finally, instead of adding the original characters of the message string to the output string, we need to add the key value to each character, making sure we get a char value that we can add to the output string:

           output += (char)(message.charAt(x) + key);

We’ve added the key value to each character in the message string, and then we’ve taken that sum and cast it to a char. Putting char in parentheses before an expression forces the value to its right to fit into a char data type, or casts the value to that type. We have to cast the output value to (char) here because in Java, the right side of this equation may result in an int value by default. Listing 6-2 shows the program with these changes.

import java.util.Scanner;
public class SecretMessages {
    public static void main(String[] args) {
       Scanner scan = new Scanner(System.in);
       System.out.println("Enter a message to encode or decode:");
       String message = scan.nextLine();
       String output = "";
       char key = 13;
       for ( int x = 0; x < message.length(); x++ ) {
           output += (char)(message.charAt(x) + key);
       }
       System.out.println(output);
    }
}

Listing 6-2: The Secret Messages app is still short at just 14 lines, but it encodes strings of text now!

If you run the program at this point and enter a message, you should see output like the following:

Enter a message to encode or decode:
Secret messages are so cool!
`rp□r?-zr??ntr?-n□r-?|-p||y.

The message is encoded, but it’s not quite what we were expecting. First of all, we’re encoding all characters, including spaces and punctuation marks. Second, we’re not wrapping around to the front of the alphabet yet, because we’re always adding 13 to every character, not taking into account the fact that the letters at the end need to wrap back around to the beginning of the alphabet, resulting in odd symbols and unprintable characters in the output message. We’ll cover both of these issues in the next section. Before moving on, save your program.

Encoding Just the Letters

Our second iteration of the Secret Messages app gave us an encoded message, but it lacks some features that we want in the final app. The upgrades needed to create the final app will require some logic in the form of if statements and conditions to encode only letters (not spaces or punctuation) and to make the alphabet wrap around during encoding. We’ll tackle these improvements in our third iteration.

First, we want to perform some tests on each character in the input message instead of just adding the character directly to the output message. Let’s change the body of the for loop as follows:

      for ( int x = 0; x < message.length(); x++ ) {
          char input = message.charAt(x);
      }

The character stored in input will be the first character in message, then the second, and so on. We need to test each character one by one to check whether it’s a character that we want to encode. We want to encode letters by adding the key value to them and wrapping to the front of the alphabet if necessary. Otherwise, if the character is a space or punctuation, we’ll leave the character unchanged and add it to the output message.

Let’s add the following if statement in the body of our for loop:

         char input = message.charAt(x);
         if (input >= 'A' && input <= 'Z')

In the condition inside the if statement, we can use either 65 (the value of 'A') or the character literal 'A' itself. The condition tests whether the character stored in input is greater than or equal to 'A' and less than or equal to 'Z'—in other words, whether input contains an uppercase letter. If this condition is true, we want to add the key value to input to shift it, producing the Caesar cipher value to substitute for that letter. Notice that this is only for uppercase letters—lowercase letters will require a separate test.

Now, in the body of the if statement, we’ll add the key value to the input character to encode the letter. This is also where we’ll handle wrapping around to the front of the alphabet if the key value shifts a letter past 'Z'. So the code inside the for loop will become:

     for ( int x = 0; x < message.length(); x++ ) {
         char input = message.charAt(x);
         if (input >= 'A' && input <= 'Z')
         {
           input += key;
           if (input > 'Z')
               input -= 26;
         }
       output += input;
     }

After getting the next character from the message and checking to make sure it’s an uppercase letter, we encode the letter by adding the key to it . Then, we check whether adding the key value pushed the letter past Z . If so, at we subtract 26 (the number of letters in the English alphabet) from the encoded input value to wrap it back around to the front of the alphabet. Finally, we can add the resulting input character to the output string .

If you run the program now, you can encode an all-uppercase message as follows:

Enter a message to encode or decode:
SECRET MESSAGES ARE SO COOL!
FRPERG  ZRFFNTRF  NER FB  PBBY!

Notice the punctuation and spaces stay the same, and all uppercase letters are shifted by 13 characters, wrapping around so that S becomes F, and so on.

Encoding the lowercase letters is logically identical to handling the uppercase letters. You can copy and paste the same if statement code, but change the A and Z to lowercase letters. Don’t forget to add an else before the second if statement. Try it yourself; but if you get stuck, the complete code is provided in Listing 6-3 in the next section.

Closing the Scanner

There’s one touch we can add here that you may recall from Chapter 2. The Scanner variable scan should have a yellow underline, indicating a warning in Eclipse telling you there is a resource leak. When you hover your mouse over the warning, it pops up with the message 'scan' is never closed. Remember that we need to close all input/output resources, such as Scanner objects, when we’re finished using them. To do so, we’ll add the command scan.close() right after the final System.out.println in the main() method of the program:

    System.out.println(output);
       scan.close();

Adding this line removes the resource leak warning. See Listing 6-3 for a fully functional Caesar cipher encoder and decoder with a key value of 13.

import java.util.Scanner;
public class SecretMessages {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        System.out.println("Enter a message to encode or decode:");
        String message = scan.nextLine();
        String output = "";
        char key = 13;
        for ( int x = 0; x < message.length(); x++ ) {
            char input = message.charAt(x);
            if (input >= 'A' && input <= 'Z')
            {
                input += key;
                if (input > 'Z')
                    input -= 26;
        }
        else if (input >= 'a' && input <= 'z')
        {
            input += key;
            if (input > 'z')
              input -= 26;
         }
          output += input;
    }
    System.out.println(output);
    scan.close();
   }
}

Listing 6-3: This version of the app is a fully functional Caesar cipher encoder and decoder with a key value of 13.

Try it out by first running the program and encoding a message. Then copy the encoded output and run the program again; when prompted, paste the encoded message and press ENTER. The program will respond with the original, decoded message. Here are two sample runs of the program in which I’ve copied the encoded output from the first message to use as the input for the second run:

Enter a message to encode or decode:
Secret messages are so cool!
Frperg zrffntrf ner fb pbby!
Enter a message to encode or decode:
Frperg zrffntrf ner fb pbby!
Secret messages are so cool!

Because the Caesar cipher is symmetric, running the program on an encoded message decodes the message back to its original plaintext. This means you can run this program as it’s written in Listing 6-3 to encode a message, send the encoded version to a friend who also has the encoder program, and have them decode your message instantly.

Unfortunately, this means anyone running the program (or anyone who figures out the cipher) can decode your messages just as quickly as you can. For our next iteration, let’s make the code a little more interesting by allowing the user to set their own key value. Then, you can pick a different key to send messages to different people, or you can use a new key every time.

Adding a Custom Key Value

The secret message encoder works well with a key value of 13, but what if we want to encode and decode messages using a different key value, such as 3 (the classical Caesar cipher shift) or 5 or 25?

We’ll need to prompt the user for a key value in addition to the message. We can do that by adding a prompt before the line that creates the char key variable:

       String output = "";
       System.out.println("Enter a secret key (-25 to 25):");
       char key = 13;

We’re allowing negative key values as a convenience for decoding messages. If you use a key value of 5 to encode your message and send it to a friend, your friend can use a key value of -5 to decode the message.

When the user responds with their desired secret key value, we can scan the line of input they provide, parse the integer value out of the line of input, and store the value in an int variable:

       String output = "";
       System.out.println("Enter a secret key (-25 to 25):");
       int keyVal = Integer.parseInt(scan.nextLine());

Finally, instead of using 13 as the key variable, we’ll change the variable key to the following:

       int keyVal = Integer.parseInt(scan.nextLine());
       char key = (char) keyVal;
       for ( int x = 0; x < message.length(); x++ ) {

Here, we’re casting the keyVal entered to a char and storing that value in the variable key, since we can’t store an integer directly into a char.

To handle negative key values for the decoding process, we’ll need to make a change to the logic inside the if statements. We have to check whether subtracting a value (or adding a negative key) would shift the letter past the beginning of the alphabet (or less than 'A'). If that happens, we can shift the letter back into the range from A to Z by adding 26. Add the following code to the if statement for uppercase letters:

        if (input >= 'A' && input <= 'Z')
        {
           input += key;
           if (input > 'Z')
               input -= 26;
           if (input < 'A')
               input += 26;
       }

If we input a negative key, we check whether we shifted before A and add 26 to wrap back around to the end of the alphabet.

Remember to do the same for the lowercase logic inside the else-if statement that follows:

        else if (input >= 'a' && input <= 'z')
        {
            input += key;
             if (input > 'z')
                 input -= 26;
             if (input < 'a')
                 input += 26;
        }

After these changes, you’ll be able to run the program to encode and decode messages with your own, custom key. Your program should work like the following example:

Enter a message to encode or decode:
You've written a really cool app in Java!
Enter a secret key (-25 to 25):
7
Fvb'cl dypaalu h ylhssf jvvs hww pu Qhch!
Enter a message to encode or decode:
Fvb'cl dypaalu h ylhssf jvvs hww pu Qhch!
Enter a secret key (-25 to 25):
-7
You've written a really cool app in Java!

The first time we ran the program, we used a key of 7. So, to decode the secret message, we ran the program again with the key value -7, revealing the original plaintext message.

Go ahead and try it out!

Encoding Digits

Now we have encoded letters, but if we encode a message like the following, any digits remain unencoded, in their original plaintext:

Enter a message to encode or decode:
Meet me at the arcade at 5pm.
Enter a secret key (-25 to 25):
8
Ummb um ib bpm izkilm ib 5xu.

Notice the 5 in 5pm remains a 5 in the output message. If we want to encode numbers as well as letters, we need to add one more section of logic to the for loop. We’ll need to check whether a character falls between 0 and 9 and then encode that character as a different digit. We’ll also have to remember to wrap back around to the front or back of the set of 10 digits. We’ll have to handle this differently from wrapping around in the set of 26 letters since we’re dealing with numbers now.

First, let’s add another else-if statement right after the else-if that handles lowercase letters:

        else if (input >= 'a' && input <= 'z')
        {
            input += key;
            if (input > 'z')
                input -= 26;
            if (input < 'a')
                input += 26;
          }
          else if (input >= '0' && input <= '9')
          output += input;

This part looks similar to the previous two if conditions, except that we’re using the digits '0' through '9', instead of the letters 'A' to 'Z', as the range.

The next line, inside the brace for the if statement, looks a bit different:

        else if (input >= '0' && input <= '9')
        {
            input += (keyVal % 10);

First, we’re using the integer version of the secret key, keyVal, which the user entered earlier in the program. Second, we’ve used the modulo operator (%) to keep the shift value for digits between -10 and +10.

We also need to test whether adding the keyVal pushed a digit past 9 or before 0, just as we checked whether encoding a letter pushed it past Z or before A. But, instead of subtracting 26 to wrap back around to the beginning of the alphabet, we need to subtract 10 to keep a digit between 0 and 9:

else if (input >= '0' && input <= '9')
            {
                input += (keyVal % 10);
                if (input > '9')
                    input -= 10;
                if (input < '0')
                    input += 10;
            }
            output += input;

If encoding a digit pushes it past 9, we subtract 10. And, if decoding moves a digit below 0, we add 10.

Now we can encode both numbers and letters in the messages we send:

Enter a message to encode or decode:
Meet me at the arcade at 5pm and bring $2 to play Pac-Man :)
Enter a secret key (-25 to 25):
7
Tlla tl ha aol hyjhkl ha 2wt huk iypun $9 av wshf Whj-Thu :)

The 5 in 5pm is correctly shifted by seven and wraps back around to 2wt in the encoded message. The 2 in $2 is shifted by seven to $9. You can test the preceding secret message by decoding it with a key of -7, and you should get the original plaintext message back, including the numbers.

The full, final text-based version of the Secret Messages app is given in Listing 6-4.

import java.util.Scanner;
public class SecretMessages {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        System.out.println("Enter a message to encode or decode:");
        String message = scan.nextLine();
        String output = "";
        System.out.println("Enter a secret key (-25 to 25):");
        int keyVal = Integer.parseInt(scan.nextLine());
        char key = (char) keyVal;
        for ( int x = 0; x < message.length(); x++ ) {
            char input = message.charAt(x);
            if (input >= 'A' && input <= 'Z')
            {
                input += key;
                if (input > 'Z')
                    input -= 26;
                if (input < 'A')
                    input += 26;
            }
            else if (input >= 'a' && input <= 'z')
            {
                input += key;
                if (input > 'z')
                    input -= 26;
                if (input < 'a')
                    input += 26;
            }
            else if (input >= '0' && input <= '9')
            {
                input += (keyVal % 10);
                if (input > '9')
                    input -= 10;
                if (input < '0')
                    input += 10;
            }
            output += input;
      }
        System.out.println(output);
        scan.close();
    }
}

Listing 6-4: The finished command line version of the Secret Messages app

This Secret Messages app can be a fun way to share scrambled messages back and forth with friends. But some of your friends may not have Eclipse or the Java JDK installed on their computer. Wouldn’t it be nice to be able to share the Secret Messages app with them too? In the next section, we’ll see how to run a command line Java app without opening Eclipse.

Running Command Line Apps Without Eclipse

We’ve built two command line apps so far, but we’ve always run them from inside Eclipse. Eclipse provides a convenient console emulator to let us see how a command line app would look and feel, but what if we wanted to run the app from an actual command line, such as the Command Prompt in Windows or the Terminal on macOS? Or what if we wanted to send one of our command line apps to a friend who doesn’t have Eclipse installed on their computer?

Fortunately, most people have at least the JRE, or Java Runtime Environment, installed on their computers.

Finding Your Workspace Folders

To run the app you’ve written and compiled in Eclipse, first start by finding your Eclipse workspace folder. Open it in File Explorer (or Finder), as shown in Figure 6-7.

Images

Figure 6-7: My workspace folder has folders inside it for each of the projects we’ve built so far, plus some Eclipse default folders.

Open the SecretMessages project folder inside your workspace, and you’ll see a few files and folders inside it, as shown in Figure 6-8. The src folder has your source code files (ending in .java), and the bin folder has the compiled version of your app (ending in .class).

Images

Figure 6-8: Find the bin folder inside the SecretMessages project folder.

Opening a Command Line Window

Next, open a command line window. On Windows, you can do this by pressing the Start button, going to the search bar, and typing cmd (short for command) and pressing ENTER. On macOS, use the Spotlight search bar to find Terminal, or from a new Finder window, go to Applications Utilities Terminal. On Linux, open the Terminal application or use Search to find Terminal.

We need to browse into the bin folder described earlier from the command line or Terminal prompt. Type cd (“change directory”) followed by a space in your terminal window. Then, to change directories into the bin folder for your app, drag the bin folder from your File Explorer or Finder window and drop it into the Command Prompt or Terminal window, as shown in Figure 6-9.

Images

Figure 6-9: You can drag and drop the bin folder from File Explorer or Finder into the command line window to easily copy the directory path.

Notice that the full path appears after the cd command and looks something like the following (your workspace/SecretMessages/bin folder will be in a slightly different location from mine, of course):

cd "C:\Users\Bryson Payne\workspace_ch06\SecretMessages\bin"

On macOS, the command will be:

cd /Users/BrysonPayne/Desktop/workspace_ch06/SecretMessages/bin

Press ENTER after this command, and the terminal will change the prompt to show you that you’re now inside the bin folder. On Windows, this looks like:

C:\Users\Bryson Payne\workspace_ch06\SecretMessages\bin>_

Now we’re inside the bin folder, where the file SecretMessages.class has been compiled. To run the program contained in the SecretMessages.class bytecode file, enter the following command:

C:\Users\Bryson Payne\workspace_ch06\SecretMessages\bin>java SecretMessages

Spelling, capitalization, and spacing all matter in the command, so you’ll need to match the name you gave your Java class precisely. If you typed the command correctly, the app should run, and you can test it with a message and secret key like so:

C:\Users\Bryson Payne\workspace_ch06\SecretMessages\bin>java SecretMessages
Enter a message to encode or decode:
I'm running my app directly from the command line terminal!
Enter a secret key (-25 to 25):
12
U'y dgzzuzs yk mbb pudqofxk rday ftq oayymzp xuzq fqdyuzmx!

You can run the app again by retyping the line java SecretMessages or by pressing the up arrow key on your keyboard and pressing ENTER. Figure 6-10 shows a couple of runs encoding and decoding a message from a Windows command prompt.

Images

Figure 6-10: Now that you can run your Java app from the command line, you can encode and decode messages on any computer with Java installed, with or without Eclipse.

Now that you know how to run the app on your computer, you can share the SecretMessages.class file with your friends to exchange encoded messages. If their computer has Java (JDK or JRE) installed, all they’ll have to do is open their command line program, change directories into the folder containing the SecretMessages.class file, and run the same java SecretMessages command. To exchange messages, just agree on a key value to use. You can choose to use the same key every time or use different key values with different friends! Remember, though, this message app is just for fun—anyone with this program, or with a little time on their hands, can break a simple Caesar cipher. We’ll see how to break Caesar ciphers easily in Chapter 7.

What You Learned

The Secret Messages app provided a fun way to delve into the world of manipulating characters and strings of text in Java. Among the new things you learned in this chapter were:

• Using the Caesar cipher to encode and decode simple messages

• Understanding the char data type for storing single Unicode characters

• Getting a specific character in a string with the charAt() method

• Accessing a specific location in a string using the index, or position number, of a character in the string

• Adding strings and characters together using the + operator

• Looping through a string with a for loop, using its length() function to get the number of characters

• Understanding how the computer stores characters and other data represented by number values

• Running command line apps directly from the command line, without Eclipse

Programming Challenges

Try these programming challenge exercises to review and practice what you’ve learned, as well as to expand your programming skills by trying something new. Visit the book’s website at https://www.nostarch.com/learnjava/ to download sample solutions.

#1: Looping the Loop

We’ve built a fun message encoder/decoder app that will let us send and receive secret texts, emails, tweets, and so on. For your first programming challenge, add a loop to the Secret Messages app that will allow you to keep encoding and decoding messages as many times as you like.

You can choose to use the same key every time, or you can ask the user for a new key each time. If you ask the user to enter a secret key each time through the loop, you can enable them to encode and decode messages, one after the other (entering 8, for example, to encode a message to a friend, and -8 to decode a message from that same friend).

One way to do this is to prompt the user to enter a new message to encode or decode or tell them to press ENTER to quit. Then, use an if statement to check whether the user entered a message or just pressed ENTER.

Give it a shot!

HINT

message.length() will be greater than zero if the user typed something.

#2: Reversing and Encoding

Let’s make our messages even harder to decipher by reversing the message before we encode it with the Caesar cipher. This double-encoding version of the app will combine the message-reversing approach of the first version in Listing 6-1 on page 130 with the Caesar cipher features of the final version from Listing 6-4 on page 140.

Reversing the order of the message is another example of symmetric encryption—doing it once reverses the message, and doing it again restores the message. Combining the string reversal with the Caesar cipher shift doesn’t make the program much more difficult, but it can make your messages even more confusing to an eavesdropper.

Make sure your app correctly encodes and decodes messages, and remember that your friends will need the new version of the program to be able to decipher your new, doubly scrambled messages! Enjoy!

#3: Safely Handling Keys with try and catch

A final improvement you could make to the Secret Messages app would be to safely handle bad numeric input when the user enters a key value. Remember the try-catch statement?

Adding the try-catch blocks to this app can be a bit tricky. In addition to adding the exception-handling code to prevent the user from crashing the program by giving a bad number (for example, by entering text instead of a number when prompted for the key value), you’ll need to think about what the app should do when bad input is provided. Should it tell the user their key was invalid and use a predefined key instead (say, 13)? Or should it create a loop that will keep asking the user for a valid key?

Try coding it both ways and pick the one you like best!