Lecture 5

Input Output

Every non-trivial application needs some way to receive input from the user and then provide the output from the computation. We already used System.out.println for printing messages on the screen but in this lecture we will go into details.

Looking at the documentation for class System, we see that it has three static variables called in, out and err. The first two refer to the objects that represent the default devices for reading input and printing output. Usually these devices are the keyboard for input and the screen for output. However, this is not necessary the case. All modern operating systems provide a way to redirect the input and the output of an application to other devices. We will soon see an example of this. The err variable points to a second output device which by convention is used only for printing errors. By default both out and err are associated with the screen but by having two different objects it is possible to redirect the normal output of the program to one device and the errors to another one.

Output

Both out and err are of type PrintStream which provides a number of useful methods along with the println method that we used so far. First of all there is the method print which is similar to println except that with println, after the string is printed, the cursor in the terminal is moved to the next line, while with print it stays one the same line. The method print is often used when one message needs to be printed in several steps.

An alternative method for printing is printf. Contrary to print and println this method provides a number of options for formatting the output. The first argument of printf is a string describing the output format, and after that follows a variable number of arguments which list all the things that need to be printed. The formatting string can be a mixture of plain text which is just printed and format specifiers which are replaced with a value taken from the list of arguments and formatted accordingly. Each format specifier starts with % and ends with a type specifier. The type specifier is one of the following letters:
CodeTypeExample
dDecimal integer123
fFixed floating-point12.30
eExponential floating-point1.23e+1
gGeneral floating-point12.3
sStringTax:
and specifies what type of argument is expected. The type specifier could be also preceded with one or more format modifiers:
FlagMeaningExample
-Left alignment1.23 followed by spaces
0Show leading zeroes001.23
+Show a plus sign for positive numbers+1.23
(Enclose negative numbers in parentheses(1.23)
,Show decimal separators12,300
^Convert letters to uppercase1.23E+1

The following is an example which prints a list of items with their names and prices. The names are formatted to be ten characters long and aligned to the left. The prices are formatted to be with width 10 digits and two digits after the decimal point:

for (int i = 0; i < items.length; i++) {
    System.out.printf("name: %-10s  price: %10.2f", items[i].name, items[i].price);
}

Input

The variable System.in is of type InputStream which is an object which allows low-level access to the standard input. Basically it only allow us to read a fixed number of bytes. Because of this the standard input is more often used in combination with the class Scanner which provides a more high-level layer permiting reading strings and numbers from the terminal. The scanner must be initialized in the following way:

Scanner scanner = new Scanner(System.in);

After that we can call the following methods:
byte nextByte()
short nextShort()
int nextInt()
long nextLong()
float nextFloat()
double nextDouble()
boolean nextBoolean()
String nextLine()

each except the last one reads a token, i.e. a sequence of characters that are not spaces, and tries to interpretet the token as a value of the corresponding type. The last method reads a full line and returns it as string.

The following example reads a string and prints it with all of its characters turned into capital letters:

Scanner scanner = new Scanner(System.in);
String line = scanner.nextLine();
System.out.println(line.toUpperCase());

Control Structures

if-then-else

The most basic control structure is the one that allows to write a program which takes different paths depending on certain decisions. We introduced the boolean type and the boolean operations (==, <, >, etc) in the previous lecture and this is what we use in combination with the if-then-else construction. This is the general syntax:

if (condition) {
     ... this code will be executed if the condition is true ...
} else {
     ... this code will be executed if the condition is false ...
}

When we don't want anything in the else block then we can skip the whole clause, i.e. we can write:

if (condition) {
     ... this code will be executed  if the condition is true ...
}

Furthermore, if there is only one operator inside one of the blocks, then we can skip the curly braces. For example:

if (richter >= 0)
  System.out.println("The richter scale is "+richter);
else
  System.out.println("Negative value is not allowed");

Omitting the curly braces has a negative effect if there are nested if statements. Look at this example:

if (richter >= 0)
  if (richter <= 4)
    System.out.println("The earthquake is harmless");
else
  System.out.println("Negative value is not allowed");

This will not have the intended effect since the else clause is always attached to the last if statement. This means that the code above will be interpreted as:

if (richter >= 0) {
  if (richter <= 4)
    System.out.println("The earthquake is harmless");
  else
    System.out.println("Negative value is not allowed");
}

In situations like this it is necessary to use the curly braces to tell the compiler what you have really intended. This is a common mistake and it is known with the name 'the dangling else'.

There are two common stylistic errors related to the if statement and they lead to uggly and cluttered code. In both cases this is due to insufficient knowledge about the language. The first problem is that new programmers sometimes does not realize that the boolean expressions are really expressions and they can be used everywhere. This leads to code like this:

boolean flag;
if (x == 0)
  flag = true;
else
  flag = false;

This is wrong! The x == 0 is an expression of type boolean and it already has a value which is either true or false. The simplest and the most readable version of this code is the following:

boolean flag = (x == 0)

It is just one line instead of five!

A similar example is the code:

if (flag == true) {
  ...
}

You don't have to check whether a boolean variable is equal to true of false. The if statement will do it for you! The right way to write it is like this:

if (flag) {
   ....
}

Thе 'switch-case' construction

The if-then-else statement let us to branch the execution of our program in two possible directions. Very often we need to have more than two branches. We can already do this by composing several if statements. For example, we might have a program which reads a number from the terminal and performs different actions depending on the value of the number:

Scanner scanner = new Scanner(System.in);
System.out.print("Choose an option (1-3):");
int choice = scanner.nextInt();
if (choice == 1) {
    System.out.println("You chose the first option.");
} else if (choice == 2) {
    System.out.println("You chose the second option.");
} else if (choice == 3) {
    System.out.println("You chose the third option.");
} else {
    System.out.println("You must choose an option from 1 to 3.");
}

Checking whether a variable has one of many predetermined values is a common pattern and it is inconvenient to write it as a sequence of if statements. For convenience, Java has the 'switch-case' statement:

Scanner scanner = new Scanner(System.in);
System.out.print("Choose an option (1-3):");
int choice = scanner.nextInt();
switch (choice) {
case 1:
    System.out.println("You chose the first option.");
    break;
case 2:
    System.out.println("You chose the second option.");
    break;
case 3:
    System.out.println("You chose the third option.");
    break;
default:
    System.out.println("You must choose an option from 1 to 3.");
}

Here after switch we can write any expression and the value of the expression will be used to decide in which direction the program must branch. In our case the expression is just 'choice'. Each case clause introduces one possible value and the statements after the case will be executed only if the computed expression has this value. The sequence of statements after the case must be terminated with the operator break. The clause default introduces a sequence of statements which will be executed only if none of the cases before that matches with the computed value. The default clause is optional.

Loops

The general syntax of the for loop is:

for (initialization; final condition; increment) {
   .....
}

The curly braces mark the beginning and the end of the body of the loop, but they are actually optional and can be omitted if there is only one statement in the loop.

In almost all cases the for loops are used with a loop variable which is set to a different value for every iteration of the loop. The loop ends when the variable reaches to some final value. The section 'initilization' is a statement which is executed only once and it sets the initial value for the variable. The 'final condition' is a logical expression which will be checked before every iteration of the loop. When the condition is violated, this will signal the end of the loop. After every iteration we must compute the new value of the variable and this is done with the statement 'increment'. The simplest example is a loop which is iterated a fixed number of times:

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

The for loop is more flexible and we can do a lot more with it. For example we might want to have a loop where the variable increases with different steps. If we want to get only the even numbers within some interval then it is enough to replace i++ with i+=1. If we want the variable to take values which are always a powers 2, then we can replace the addition with multiplication:

for (long i = 1; i < Integer.MAX_VALUE; i *= 2) {
    System.out.println(i);
}

This loop will print all powers of two from 1 to the maximal number that can be stored in a type int.

Question: Why we cannot use a variable of type of int here? If you have doubts, read about integer overflows from the previous lecture.

Both the initialization, the final condition and the increment are optional. For example, we can skip all of them and this will lead to an infinite loop:

for (;;) {
    System.out.println("Hello");
}

This will lead to a program which never terminates and instead prints the string "Hello" indefinitely.

We can also have for loops that have only condition but no initialization or increment. In this case the loop will simply iterate until some condition becomes true. Let's take as an example the problem of finding how many years a certain amout of money must stay in the bank until it reaches some target balance. We can compute it in the following way:

int years = 0;
double balance = .... the initial balance ...
double targetBalance = .... how much we want at the end ...
double rate = ... the early interest rate from the bank ...
for (; balance < targetBalance ;) {
    years++;
    balance += balance * rate / 100;
}

System.out.println(years);

Since this kind of pattern, i.e. a loop which has only a condition is very common, there is also an alternative and cleaner syntax:

int years = 0;
double balance = .... the initial balance ...
double targetBalance = .... how much we want at the end ...
double rate = ... the yearly interest rate from the bank ...
while (balance < targetBalance) {
    years++;
    balance += balance * rate / 100;
}
System.out.println(years);

This kind of loop is called a while loop and it makes it more obvious that we just want to iterate until some condition is met. In principle we can always replace every while loop with a for loop.

There is also another variation of the while loop which is more difficult to emulate with a for loop. This is the so called 'do-while' loop. Again this is a loop which iterates until some condition is satisfied, but the difference is that the condition is checked at the end of the loop rather than at the beginning. This is sometime useful if the variables that we use in the exit condition are not initialized yet in the beginning of the loop. For example we might have a program which asks for a password, and it must ask again and again until the right password is given. The easiest way to do this is:

String password;
Scanner scanner = new Scanner(System.in);
do {
    System.out.print("Password: ");
    password = scanner.nextLine();
} while (!password.equals("123"));

Here the variable 'password' contains the keyword that the user has entered, but there is no point to check the value of the variable in the beginning of the loop since it is not set yet.

Sometimes we want to break the execution of a loop forcefully from inside the loop. This is possible by using the statement break. For example we could consider a variant of the program for passwords which allows up to three attempts for authorization and after that denies the access:

int attempts = 0;
String password;
Scanner scanner = new Scanner(System.in);
do {
    attempts++;
    if (attempts > 3)
      break;

    System.out.print("Password: ");
    password = scanner.nextLine();
} while (!password.equals("123"));

if (attempts > 3)
  System.out.println("Access denied!");
else
  System.out.println("Access granted!");

Here we have two conditions for terminating the loop and one of them have to be checked in the beginning of the loop while the other have to be checked at the end. In situations like this we can choose one of the conditions to be part of the loop and the other to be enforced by using the break statement. We have already seen this statement as part of the 'switch-case' construction but here it is used for a bit different purpose.

A similar situation is when we want forcefully to terminate the current iteration but we still want to continue with the next iteration instead of terminating the whole loop. For this we have the statement continue. It terminates the current iteration and starts a new one.