Tuesday 7 March 2017

Java’s Selection Statements - Java Tutorials

Java supports two selection statements: if and switch. These statements allow you to control the flow of your program’s execution based upon conditions known only during run time. You will be pleasantly surprised by the power and flexibility contained in these two statements.

if

The if statement was introduced in Chapter 2. It is examined in detail here. The if statement is Java’s conditional branch statement. It can be used to route program execution through two different paths. Here is the general form of the if statement:

  if (condition) statement1;
  else statement2;

Here, each statement may be a single statement or a compound statement enclosed in curly braces (that is, a block). The condition is any expression that returns a boolean value. The else clause is optional.

The if works like this: If the condition is true, then statement1 is executed. Otherwise, statement2 (if it exists) is executed. In no case will both statements be executed. For example, consider the following:

  int a, b;
  // ...
  if(a < b) a = 0;
  else b = 0;

Here, if a is less than b, then a is set to zero. Otherwise, b is set to zero. In no case are they both set to zero.

Most often, the expression used to control the if will involve the relational operators. However, this is not technically necessary. It is possible to control the if using a single boolean variable, as shown in this code fragment:

  boolean dataAvailable;
  // ...
  if (dataAvailable)
    ProcessData();
  else
    waitForMoreData();

Remember, only one statement can appear directly after the if or the else. If you want to include more statements, you’ll need to create a block, as in this fragment:

  int bytesAvailable;
  // ...
  if (bytesAvailable > 0) {
    ProcessData();
    bytesAvailable -= n;
  } else
    waitForMoreData();

Here, both statements within the if block will execute if bytesAvailable is greater than zero.

Some programmers find it convenient to include the curly braces when using the if, even when there is only one statement in each clause. This makes it easy to add another statement at a later date, and you don’t have to worry about forgetting the braces. In fact, forgetting to define a block when one is needed is a common cause of errors. For example, consider the following code fragment:

  int bytesAvailable;
  // ...
  if (bytesAvailable > 0) {
    ProcessData();
    bytesAvailable -= n;
  } else
    waitForMoreData();
    bytesAvailable = n;

It seems clear that the statement bytesAvailable = n; was intended to be executed inside the else clause, because of the indentation level. However, as you recall, whitespace is insignificant to Java, and there is no way for the compiler to know what was intended. This code will compile without complaint, but it will behave incorrectly when run. The preceding example is fixed in the code that follows:

  int bytesAvailable;
  // ...
  if (bytesAvailable > 0) {
    ProcessData();
    bytesAvailable -= n;
  } else {
    waitForMoreData();
    bytesAvailable = n;
  }


Nested ifs

A nested if is an if statement that is the target of another if or else. Nested ifs are very common in programming. When you nest ifs, the main thing to remember is that an else statement always refers to the nearest if statement that is within the same block as the else and that is not already associated with an else. Here is an example:

  if(i == 10) {
    if(j < 20) a = b;
    if(k > 100) c = d; // this if is
    else a = c;        // associated with this else
  }
  else a = d;          // this else refers to if(i == 10)

As the comments indicate, the final else is not associated with if(j<20), because it is not in the same block (even though it is the nearest if without an else). Rather, the final else is associated with if(i==10). The inner else refers to if(k>100), because it is the closest if within the same block.

The if-else-if Ladder

A common programming construct that is based upon a sequence of nested ifs is the if-else-if ladder. It looks like this:

  if(condition)
   statement;
  else if(condition)
   statement;
  else if(condition)
   statement;
  .
  .
  .
  else
   statement;

The if statements are executed from the top down. As soon as one of the conditions controlling the if is true, the statement associated with that if is executed, and the rest of the ladder is bypassed. If none of the conditions is true, then the final else statement will be executed. The final else acts as a default condition; that is, if all other conditional tests fail, then the last else statement is performed. If there is no final else and all other conditions are false, then no action will take place.

Here is a program that uses an if-else-if ladder to determine which season a particular month is in.

  // Demonstrate if-else-if statements.
  class IfElse {
    public static void main(String args[]) {
      int month = 4; // April
      String season;

      if(month == 12 || month == 1 || month == 2)
        season = "Winter";
      else if(month == 3 || month == 4 || month == 5)
        season = "Spring";
      else if(month == 6 || month == 7 || month == 8)
        season = "Summer";
      else if(month == 9 || month == 10 || month == 11)
        season = "Autumn";
      else
        season = "Bogus Month";
      System.out.println("April is in the " + season + ".");
    }
  }

Here is the output produced by the program:

  April is in the Spring.

You might want to experiment with this program before moving on. As you will find, no matter what value you give month, one and only one assignment statement within the ladder will be executed.

switch

The switch statement is Java’s multiway branch statement. It provides an easy way to dispatch execution to different parts of your code based on the value of an expression. As such, it often provides a better alternative than a large series of if-else-if statements. Here is the general form of a switch statement:

  switch (expression) {
    case value1:
      // statement sequence
     break;
    case value2:
      // statement sequence
     break;
   .
   .
   .
    case valueN:
      // statement sequence
     break;
    default:
      // default statement sequence
   }


The expression must be of type byte, short, int, or char; each of the values specified in the case statements must be of a type compatible with the expression. Each case value must be a unique literal (that is, it must be a constant, not a variable). Duplicate case values are not allowed.

The switch statement works like this: The value of the expression is compared with each of the literal values in the case statements. If a match is found, the code sequence following that case statement is executed. If none of the constants matches the value of the expression, then the default statement is executed. However, the default statement is optional. If no case matches and no default is present, then no further action is taken.

The break statement is used inside the switch to terminate a statement sequence. When a break statement is encountered, execution branches to the first line of code that follows the entire switch statement. This has the effect of “jumping out” of the switch.

Here is a simple example that uses a switch statement:

  // A simple example of the switch.
  class SampleSwitch {
    public static void main(String args[]) {
      for(int i=0; i<6; i++)
        switch(i) {
          case 0:
            System.out.println("i is zero.");
            break;
          case 1:
            System.out.println("i is one.");
            break;
          case 2:
            System.out.println("i is two.");
            break;
          case 3:
            System.out.println("i is three.");
            break;
          default:
            System.out.println("i is greater than 3.");
        }
    }
  }

The output produced by this program is shown here:

  i is zero.
  i is one.
  i is two.
  i is three.
  i is greater than 3.
  i is greater than 3.

As you can see, each time through the loop, the statements associated with the case constant that matches i are executed. All others are bypassed. After i is greater than 3, no case statements match, so the default statement is executed.

The break statement is optional. If you omit the break, execution will continue on into the next case. It is sometimes desirable to have multiple cases without break statements between them. For example, consider the following program:

  // In a switch, break statements are optional.
  class MissingBreak {
    public static void main(String args[]) {
      for(int i=0; i<12; i++)
        switch(i) {
          case 0:
          case 1:
          case 2:
          case 3:
          case 4:
            System.out.println("i is less than 5");
            break;
          case 5:
          case 6:
          case 7:
          case 8:
          case 9:
            System.out.println("i is less than 10");
            break;
          default:
            System.out.println("i is 10 or more");
        }
    }
  }

This program generates the following output:

  i is less than 5
  i is less than 5
  i is less than 5
  i is less than 5
  i is less than 5
  i is less than 10
  i is less than 10
  i is less than 10
  i is less than 10
  i is less than 10
  i is 10 or more
  i is 10 or more

As you can see, execution falls through each case until a break statement (or the end of the switch) is reached.

While the preceding example is, of course, contrived for the sake of illustration, omitting the break statement has many practical applications in real programs. To sample its more realistic usage, consider the following rewrite of the season example shown earlier. This version uses a switch to provide a more efficient implementation.

  // An improved version of the season program.
  class Switch {
    public static void main(String args[]) {
      int month = 4;
      String season;
      switch (month) {
        case 12:
        case 1:
        case 2:
          season = "Winter";
          break;
        case 3:
        case 4:
        case 5:
          season = "Spring";
          break;
        case 6:
        case 7:
        case 8:
          season = "Summer";
          break;
        case 9:
        case 10:
        case 11:
          season = "Autumn";
          break;
        default:
          season = "Bogus Month";
      }
      System.out.println("April is in the " + season + ".");
    }
  }


Nested switch Statements

You can use a switch as part of the statement sequence of an outer switch. This is called a nested switch. Since a switch statement defines its own block, no conflicts arise between the case constants in the inner switch and those in the outer switch. For example, the following fragment is perfectly valid:

  switch(count) {
    case 1:
      switch(target) { // nested switch
        case 0:
          System.out.println("target is zero");
          break;
        case 1: // no conflicts with outer switch
          System.out.println("target is one");
          break;
      }
      break;
    case 2: // ...

Here, the case 1: statement in the inner switch does not conflict with the case 1: statement in the outer switch. The count variable is only compared with the list of cases at the outer level. If count is 1, then target is compared with the inner list cases. In summary, there are three important features of the switch statement to note:
  • The switch differs from the if in that switch can only test for equality, whereas if can evaluate any type of Boolean expression. That is, the switch looks only for a match between the value of the expression and one of its case constants.
  • No two case constants in the same switch can have identical values. Of course, a switch statement enclosed by an outer switch can have case constants in common.
  • A switch statement is usually more efficient than a set of nested ifs.

The last point is particularly interesting because it gives insight into how the Java compiler works. When it compiles a switch statement, the Java compiler will inspect each of the case constants and create a “jump table” that it will use for selecting the path of execution depending on the value of the expression. Therefore, if you need to select among a large group of values, a switch statement will run much faster than the equivalent logic coded using a sequence of if-elses. The compiler can do this because it knows that the case constants are all the same type and simply must be compared for equality with the switch expression. The compiler has no such knowledge of a long list of if expressions.

No comments:

Post a Comment