Statements in Java
Till now we have seen two types of executable statements (without counting declarations):
• method invocation
• assignment
These are simple statements by means of which we can write programs
• constituted by sequences of simple statements;
• that do method calls, possibly nested.
Very often, when we have to solve a problem, we are interested in performing different actions depending on
whether certain conditions are true or false.
Conditional statements
Java, like all other programming languages, is equipped with specific statements that allow us to check a
condition and execute certain parts of code depending on whether the condition is true or false. Such statements
are called conditional, and are a form of composite statement.
In Java, there are two forms of conditional statements:
• the if-else statement, to choose between two alternatives;
• the switch statement, to choose between multiple alternatives.
The if-else statement
The if-else statement allows us to select between two alternatives.
if-else statement
Syntax:
if (condition )
then-statement
else
else-statement
• condition is an expression of type boolean, i.e., a conditional expression that is evaluated to true or
false
• then-statement is a single statement (also called the then-branch of the if-else statement)
• else-statement is a single statement (also called the else-branch of the if-else statement)
12 UNIT 5
Semantics:
First, the condition is evaluated. If the result of the evaluation is the value true, the then-statement is
executed, otherwise the else-statement is executed. In both cases, the execution continues with the statement
immediately following the if-else statement.
Example:
int a, b;
…
if (a > b)
System.out.println(“bigger value = ” + a);
else
System.out.println(“bigger value = ” + b);
When this if-else statement is executed, the string “bigger value = ” followed by the bigger one among a
and b is printed on the output channel (the monitor).
Condition in an if-else statement
The condition in an if-else statement can be an arbitrary expression of type boolean, for example:
• a variable of type boolean;
Example:
boolean finished;
…
if (finished)
…
• one of the comparison operators (==, !=, >, <, >=, or <=) applied to variables (or expressions) of a primitive
type;
Example:
int a, b, c;
…
if (a > b + c)
…
• a call to a predicate (i.e., a method that returns a value of type boolean);
Example:
String answer;
…
if (answer.equalsIgnoreCase(“YES”))
…
• a complex boolean expression, obtained by applying the boolean operators !, &&, and || to simpler
expressions;
Example:
int a, b, c, d;
String answer;
…
if ((a > (b+c)) || (a == d) && !answer.equalsIgnoreCase(“YES”))
…
The if variant
The else part of an if-else statement is optional. If it is missing, we have an if statement, which allows us
to execute a certain part of code if a condition is satisfied (and do nothing otherwise).
if statement
Syntax:
if (condition )
then-statement
• condition is an expression of type boolean
• then-statement is a single statement (also called the then-branch of the if statement)
Semantics:
First, the condition is evaluated. If the result of the evaluation is the value true, the then-statement is
executed, and the execution continues with the statement immediately following the if statement. Otherwise,
the execution continues directly with the statement following the if statement.
Example:
boolean found;
…
if (!found)
System.out.println(“element not found”);
When this if statement is executed, the string “element not found” is printed on the output channel, provided
the value of the boolean variable found is false.
Block of statements
The syntax of if-else allows us to have only a single statement in the then-branch (or the else-branch). If
we want to execute more than one statement in the then-branch (or the else-branch), we have to use a block
construct. A block of statements groups several statements in a single composite statement.
Block of statements
Syntax:
{
statement
…
statement
}
• statement is an arbitrary Java statement
Semantics:
The statements in the block are executed in sequence. The variables declared inside the block are not visible
outside the block itself.
Example:
int a, b, bigger;
…
if (a > b) {
bigger = a;
System.out.println(“smaller = ” + b);
}
Scope of variables defined in a block
A block of statements can contain variable declarations. The scope of a variable declared inside a block is the
block itself, including other blocks contained in it, if present. This means that the variable is visible in the block
and in all sub-blocks, but is not visible outside the block.
Example:
public class ScopeInBlock {
public static void main(String[] args) {
String a = “Hello”;
int i = 1;
{
System.out.println(a);
// OK. a is visible – prints Hello
//int i;
// ERROR. i is visibile and cannot be redeclared
{
double r = 5.5; // OK
i = i + 1; // OK. i is still visible
System.out.println(r); // OK. r is visible – prints 5.5
}
//System.out.println(r); // ERROR. r is not visible
System.out.println(i); // OK. i is visibile – prints 2
{
int r = 4; // OK. previous r is not visible anymore
System.out.println(a);
// OK. a is still visibile – prints Hello
}
}
i = i + 1; // OK. i is visible
System.out.println(i); // OK. i is visible – prints 3
}
}
Use of blocks in an if-else statement
The then-branch or the else-branch of an if-else statement can be any Java statement, and in particular it
can be a block.
Example: Given month and year, compute month and year of the next month.
int month, year, nextMonth, nextYear;
…
if (month == 12) {
nextMonth = 1;
nextYear = year + 1;
} else {
nextMonth = month + 1;
nextYear = year;
}
Nested if ’s
We have a nested if when the then-branch or the else-branch of an if-else statement is again an if-else or
an if statement.
Example: Given day, month, and year, compute day, month, and year of the next day.
int day, month, year, nextDay, nextMonth, nextYear;
…
if (month == 12) {
if (day == 31) {
nextDay = 1;
nextMonth = 1;
nextYear = year + 1;
} else {
nextDay = Day + 1;
nextMonth = month;
nextYear = year;
}
} else {
…
}
Nested if ’s with mutually excluding conditions
A common use of nested if’s is when the conditions in the nested if’s are mutually excluding, i.e., no two of
them can be simultaneously true.
Example: Based on the value of the temperature (an integer) print a message according to the following table:
temperature t message
30 < t hot
20 < t ≤ 30 warm
10 < t ≤ 20 fine
t ≤ 10 cold
int temp;
…
if (30 < temp)
System.out.println(“hot”);
else if (20 < temp)
System.out.println(“warm”);
else if (10 < temp)
System.out.println(“fine”);
else
System.out.println(“cold”);
Observations:
• At the outermost level we have a single if-else statement.
• The order in which the conditions are specified is important.
• The second condition need not be composite, e.g., (20 < temp) && (temp <= 30), since it appears in
the else-branch of the first condition. Hence, we already know that (temp <= 30) is true.
• Each else refers to the if that immediately precedes it.
5.11 Ambiguity of the else in if-else statements
Consider the following code fragment:
if (a > 0) if (b > 0) System.out.println(“b positive”);
else System.out.println(“???”);
System.out.println(“???”) could in principle be the else-branch of:
• the first if: hence we should replace “???” with “a negative”;
• the second if: hence we should replace “???” with “b negative”.
The ambiguity is solved by considering that an else always refers to the nearest if without an associated
else. In the above example we have:
if (a > 0)
if (b > 0)
System.out.println(“b positive”);
else
System.out.println(“b negative”);
It is always possible to use a block (i.e., {..}) to disambiguate nested if-else statements. In particular, if we
want that an else refers to an if that is not the immediately preceding one, we have to enclose the immediately
preceding if in a block. For example:
if (a > 0) {
if (b > 0)
System.out.println(“b positive”);
} else
System.out.println(“a negative”);
Example: type of a triangle
Given three values representing the lengths of the three sides of a triangle, determine whether the triangle is
regular (all three sides are equal), symmetric (two sides are equal), or irregular (no two sides are equal).
A possible algorithm is the following: compare the sides two by two, until we have gathered sufficient information
to decide the type of the triangle.
The algorithm can be implemented as follows:
double first, second, third;
…
if (first == second) {
if (second == third)
System.out.println(“regular”);
else
System.out.println(“symmetric”);
} else {
if (second == third)
System.out.println(“symmetric”);
else if (first == third)
System.out.println(“symmetric”);
else
System.out.println(“irregular”);
}
Shortcut evaluation of a complex condition
The condition in an if-else statement can be a complex boolean expression, in which the logical operators &&,
||, and ! may appear. We have seen that Java performs a shortcut evaluation of such expressions. In other
words, the subexpressions to which the operators are applied are evaluated from left to right as follows:
• when evaluating (e1 && e2), if the evaluation of e1 returns false, then e2 is not evaluated.
• when evaluating (e1 || e2), if the evaluation of e1 returns true, then e2 is not evaluated.
Consider the case of (e1 && e2). If the value of e1 is false, then the value of the whole expression (e1 && e2)
is false, independently of the value of e2. This justifies why in Java e2 is not even evaluated. Similar
considerations hold for (e1 || e2).
In general, the fact that Java performs a shortcut evaluation of boolean expressions has to be taken into account
and cannot be ignored, since the correctness of the code may depend on that.
Example:
String s;
…
if (s != null && s.length() > 0) {
System.out.println(s);
}
In this case, when the value of s is null then s.length()>0 is not evaluated and the method length() is not
called. Note that, if Java evaluated s.length()>0 also when s is null, then the above code would be wrong,
since it would cause trying to access via s a nonexistent object.
Note: In general, if-else statements that make use of complex boolean conditions could be rewritten by
making use of nested if-else statements. However, to do so, it may be necessary to duplicate code. We
illustrate this in the following separately for && and ||.
Eliminating the conjunction operator && in a complex condition
The code fragment
if ((x < y) && (y < z))
System.out.println(“y is between x and z”);
else
System.out.println(“y is not between x and z”);
corresponds to
if (x < y)
if (y < z)
System.out.println(“y is between x and z”);
else
System.out.println(“y is not between x and z”);
else
System.out.println(“y is not between x and z”);
In this case, by eliminating the complex condition, the code of the else-branch must be duplicated.
Note that, due to shortcut evaluation, the second condition in the && is not evaluated if the first condition is
false. And this holds also for the corresponding nested if-else statements.
Eliminating the disjunction operator || in a complex condition
The code fragment
if ((x == 1) || (x == 2))
System.out.println(“x equal to 1 or to 2”);
else
System.out.println(“x different from 1 and from 2”);
corresponds to
if (x == 1)
System.out.println(“x equal to 1 or to 2”);
else if (x == 2)
System.out.println(“x equal to 1 or to 2”);
else
System.out.println(“x different from 1 and from 2”);In this case, by eliminating the complex condition, the code of the then-branch must be duplicated.
Again, due to shortcut evaluation, the second condition in the || is not evaluated if the first condition is true.
And this holds also for the corresponding nested if-else statements.
The switch Statement
Unlike if-then
and if-then-else
statements, the switch
statement can have a number of possible execution paths. A switch
works with the byte
, short
, char
, and int
primitive data types. It also works with enumerated types , the String
class, and a few special classes that wrap certain primitive types: Character,Byte,Short
, and Integer
.
The following code example, SwitchDemo
, declares an int
named month
whose value represents a month. The code displays the name of the month, based on the value of month
, using theswitch
statement.
public class SwitchDemo { public static void main(String[] args) { int month = 8; String monthString; switch (month) { case 1: monthString = "January"; break; case 2: monthString = "February"; break; case 3: monthString = "March"; break; case 4: monthString = "April"; break; case 5: monthString = "May"; break; case 6: monthString = "June"; break; case 7: monthString = "July"; break; case 8: monthString = "August"; break; case 9: monthString = "September"; break; case 10: monthString = "October"; break; case 11: monthString = "November"; break; case 12: monthString = "December"; break; default: monthString = "Invalid month"; break; } System.out.println(monthString); } }
In this case, August
is printed to standard output.
The body of a switch
statement is known as a switch block. A statement in the switch
block can be labeled with one or more case
or default
labels. The switch
statement evaluates its expression, then executes all statements that follow the matching case
label.
You could also display the name of the month with if-then-else
statements:
int month = 8; if (month == 1) { System.out.println("January"); } else if (month == 2) { System.out.println("February"); } ... // and so on
Deciding whether to use if-then-else
statements or a switch
statement is based on readability and the expression that the statement is testing. An if-then-else
statement can test expressions based on ranges of values or conditions, whereas a switch
statement tests expressions based only on a single integer, enumerated value, or String
object.
Another point of interest is the break
statement. Each break
statement terminates the enclosing switch
statement. Control flow continues with the first statement following the switch
block. The break
statements are necessary because without them, statements in switch
blocks fall through: All statements after the matching case
label are executed in sequence, regardless of the expression of subsequent case
labels, until a break
statement is encountered. The program SwitchDemoFallThrough
shows statements in a switch
block that fall through. The program displays the month corresponding to the integer month
and the months that follow in the year:
public class SwitchDemoFallThrough { public static void main(String[] args) { java.util.ArrayList<String> futureMonths = new java.util.ArrayList<String>(); int month = 8; switch (month) { case 1: futureMonths.add("January"); case 2: futureMonths.add("February"); case 3: futureMonths.add("March"); case 4: futureMonths.add("April"); case 5: futureMonths.add("May"); case 6: futureMonths.add("June"); case 7: futureMonths.add("July"); case 8: futureMonths.add("August"); case 9: futureMonths.add("September"); case 10: futureMonths.add("October"); case 11: futureMonths.add("November"); case 12: futureMonths.add("December"); break; default: break; } if (futureMonths.isEmpty()) { System.out.println("Invalid month number"); } else { for (String monthName : futureMonths) { System.out.println(monthName); } } } }
This is the output from the code:
August September October November December
Technically, the final break
is not required because flow falls out of the switch
statement. Using a break
is recommended so that modifying the code is easier and less error prone. Thedefault
section handles all values that are not explicitly handled by one of the case
sections.
The following code example, SwitchDemo2
, shows how a statement can have multiple case
labels. The code example calculates the number of days in a particular month:
class SwitchDemo2 { public static void main(String[] args) { int month = 2; int year = 2000; int numDays = 0; switch (month) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: numDays = 31; break; case 4: case 6: case 9: case 11: numDays = 30; break; case 2: if (((year % 4 == 0) && !(year % 100 == 0)) || (year % 400 == 0)) numDays = 29; else numDays = 28; break; default: System.out.println("Invalid month."); break; } System.out.println("Number of Days = " + numDays); } }
This is the output from the code:
Number of Days = 29
Using Strings in switch Statements
In Java SE 7 and later, you can use a String
object in the switch
statement’s expression. The following code example, StringSwitchDemo
, displays the number of the month based on the value of the String
named month
:
public class StringSwitchDemo { public static int getMonthNumber(String month) { int monthNumber = 0; if (month == null) { return monthNumber; } switch (month.toLowerCase()) { case "january": monthNumber = 1; break; case "february": monthNumber = 2; break; case "march": monthNumber = 3; break; case "april": monthNumber = 4; break; case "may": monthNumber = 5; break; case "june": monthNumber = 6; break; case "july": monthNumber = 7; break; case "august": monthNumber = 8; break; case "september": monthNumber = 9; break; case "october": monthNumber = 10; break; case "november": monthNumber = 11; break; case "december": monthNumber = 12; break; default: monthNumber = 0; break; } return monthNumber; } public static void main(String[] args) { String month = "August"; int returnedMonthNumber = StringSwitchDemo.getMonthNumber(month); if (returnedMonthNumber == 0) { System.out.println("Invalid month"); } else { System.out.println(returnedMonthNumber); } } }
The output from this code is 8
.
The String
in the switch
expression is compared with the expressions associated with each case
label as if the String.equals method were being used. In order for theStringSwitchDemo
example to accept any month regardless of case, month
is converted to lowercase (with the toLowerCase
method), and all the strings associated with the case
labels are in lowercase.