• Tidak ada hasil yang ditemukan

In any program, statements perform the real magic. Statements help us implement those algorithms we mentioned at the beginning of this chapter. In fact, they don’t just help, they are precisely the programming ingredient we use; each step in an algo‐

rithm will correspond to one or more statements. Statements generally do one of four things: gather input to assign to a variable, write output (to your terminal, to a JLa bel, etc.), make a decision about which statements to execute, or repeat one or more other statements. Let’s look at examples of each category in Java.

Statements and expressions in Java appear within a code block. A code block is syntac‐

tically a series of statements surrounded by an open curly brace ({) and a close curly brace (}). The statements in a code block can include variable declarations and most of the other sorts of statements and expressions we mentioned earlier:

{

int size = 5;

setName("Max");

...

}

Methods, which look like C functions, are in a sense just code blocks that take param‐

eters and can be called by their names—for example, the method setUpDog():

setUpDog( String name ) { int size = 5;

setName( name );

...

}

Variable declarations are limited in scope to their enclosing code block—that is, they can’t be seen outside of the nearest set of braces:

{

int i = 5;

}

i = 6; // Compile-time error, no such variable i

In this way, code blocks can be used to arbitrarily group other statements and vari‐

ables. The most common use of code blocks, however, is to define a group of state‐

ments for use in a conditional or iterative statement.

if/else conditionals

One of the key concepts in programming is the notion of making a decision. “If this file exists…” or “If the user has a WiFi connection…” are examples of the decisions computer programs and apps make all the time. We can define an if/else clause as follows:

if ( condition ) statement;

else

statement;

The whole of the preceding example is itself a statement and could be nested within another if/else clause. The if clause has the common functionality of taking two different forms: a “one-liner” or a block. The block form is as follows:

if ( condition ) { [ statement; ] [ statement; ] [ ... ] } else {

[ statement; ] [ statement; ] [ ... ] }

The condition is a Boolean expression. A Boolean expression is a true or false value or an expression that evaluates to one of those. For example, i == 0 is a Boolean expression that tests whether the integer i holds the value 0.

In the second form, the statements are in code blocks, and all their enclosed state‐

ments are executed if the corresponding (if or else) branch is taken. Any variables declared within each block are visible only to the statements within the block. Like the if/else conditional, most of the remaining Java statements are concerned with controlling the flow of execution. They act for the most part like their namesakes in other languages.

switch statements

Many languages support a “one of many” conditional commonly known as a “switch”

or “case” statement. Given one variable or expression, a switch statement provides multiple options that might match. The first match wins, so ordering is important.

And we do mean might. A value does not have to match any of the switch options; in that case nothing happens.

The most common form of the Java switch statement takes an integer (or a numeric type argument that can be automatically “promoted” to an integer type), a string type argument, or an “enum” type (discussed shortly) and selects among a number of alternative, constant case branches:3

switch ( expression ) {

case constantExpression :

statement;

[ case constantExpression : statement; ]

...

[ default : statement; ] }

The case expression for each branch must evaluate to a different constant integer or string value at compile time. Strings are compared using the String equals() method, which we’ll discuss in more detail in Chapter 8. An optional default case can be specified to catch unmatched conditions. When executed, the switch simply finds the branch matching its conditional expression (or the default branch) and exe‐

cutes the corresponding statement. But that’s not the end of the story. Perhaps coun‐

terintuitively, the switch statement then continues executing branches after the matched branch until it hits the end of the switch or a special statement called break. Here are a couple of examples:

int value = 2;

switch( value ) { case 1:

System.out.println( 1 );

case 2:

System.out.println( 2 );

case 3:

System.out.println( 3 );

}

// prints 2, 3!

Using break to terminate each branch is more common:

int retValue = checkStatus();

switch ( retVal ) {

case MyClass.GOOD : // something good break;

case MyClass.BAD : // something bad break;

default :

// neither one break;

}

In this example, only one branch—GOOD, BAD, or the default—is executed. The “fall through” behavior of the switch is justified when you want to cover several possible

case values with the same statement without resorting to a bunch of if/else statements:

int value = getSize();

String size = "Unknown";

switch( value ) { case MINISCULE:

case TEENYWEENIE:

case SMALL:

size = "Small";

break;

case MEDIUM:

size = "Medium";

break;

case LARGE:

case EXTRALARGE:

size = "Large";

break;

}

System.out.println("Your size is: " + size);

This example effectively groups the six possible values into three cases. And this grouping feature can now appear directly in expressions. Java 12 offers a preview of a switch expression. For example, rather than printing out the size names in the example above, we could create a new variable for the size, like this:

int value = getSize();

String size = switch( value ) { case MINISCULE:

case TEENYWEENIE:

case SMALL:

break "Small";

case MEDIUM:

break "Medium";

case LARGE:

case EXTRALARGE:

break "Large";

}

System.out.println("Your size is: " + size);

Note how we used the break statement with a value this time. You can also use a new syntax within the switch statement to make things a little more compact and maybe more readable:

int value = getSize();

}

System.out.println("Your size is: " + size);

These expressions are obviously new to the language (Java 12 even requires you to compile with the --enable-preview flag to use them) so you might not find them used very often in the online resources and examples we noted earlier. But you will definitely find good examples devoted to explaining the power of switch expressions if this statement tickles your conditional fancy.

do/while loops

The other major concept in controlling which statement gets executed next (“control flow” in computer programmerese) is repetition. Computers are really good at doing things over and over. Repeating a block of code is done with a loop. There are two main varieties of loop in Java. The do and while iterative statements run while a Boolean expression returns a true value:

while ( condition ) statement;

do

statement;

while ( condition );

A while loop is perfect for waiting on some external condition, such as getting email:

while( mailQueue.isEmpty() ) wait();

Of course, the wait() method needs to have a limit (typically a time limit such as waiting for one second) so that it finishes and gives the loop another chance to run.

But once you do have some email, you also want to process all of the messages that arrived, not just one. Again, a while loop is perfect:

while( !mailQueue.isEmpty() ) {

EmailMessage message = mailQueue.takeNextMessage();

String from = message.getFromAddress();

System.out.println("Processing message from " + from);

message.doSomethingUseful();

}

In this little snippet, we use the boolean ! operator to negate the previous test. We want to keep working while there is something in the queue. That question is often expressed in programming as “not empty” rather than “has something.” Also, note that the body of the loop is more than one statement so we put it inside the curly braces. Inside those braces, we remove the next message from the queue and store it in a local variable (message above). Then we do a few things with our message and

“loop back” to the condition to see if the queue is empty yet. If it is not empty, we repeat the whole process, starting with taking the next available message.

Unlike while or for loops (which we’ll see next) that test their conditions first, a do- while loop (or more often just a do loop) always executes its statement body at least once. A classic example is validating input from a user or maybe a website. You know you need to get some information, so request that information in the body of the loop. The loop’s condtion can test for errors. If there’s a problem, the loop will start over and request the information again. That process can repeat until your request comes back without an error and you know you have good information.

The for loop

The most general form of the for loop is also a holdover from the C language:

for ( initialization; condition; incrementor ) statement;

The variable initialization section can declare or initialize variables that are limited to the scope of the for statement. The for loop then begins a possible series of rounds in which the condition is first checked and, if true, the body statement (or block) is executed. Following each execution of the body, the incrementor expressions are evaluated to give them a chance to update variables before the next round begins:

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

int j = i;

...

}

This loop will execute 100 times, printing values from 0 to 99. Note that the variable j is local to the block (visible only to statements within it) and will not be accessible to the code “after” the for loop. If the condition of a for loop returns false on the first check, the body and incrementor section will never be executed.

You can use multiple comma-separated expressions in the initialization and incre‐

mentation sections of the for loop. For example:

for (int i = 0, j = 10; i < j; i++, j-- ) { System.out.println(i + " < " + j);

...

}

You can also initialize existing variables from outside the scope of the for loop within the initializer block. You might do this if you wanted to use the end value of the loop variable elsewhere, but generally this practice is frowned upon as prone to mistakes; it

int x;

for( x = 0; hasMoreValue(); x++ ) { getNextValue();

}

// x is still valid and available System.out.println( x );

The enhanced for loop

Java’s auspiciously dubbed “enhanced for loop” acts like the foreach statement in some other languages, iterating over a series of values in an array or other type of collection:

for ( varDeclaration : iterable ) statement;

The enhanced for loop can be used to loop over arrays of any type as well as any kind of Java object that implements the java.lang.Iterable interface. This includes most of the classes of the Java Collections API. We’ll talk about arrays in this and the next chapter; Chapter 7 covers Java Collections. Here are a couple of examples:

int [] arrayOfInts = new int [] { 1, 2, 3, 4 };

for( int i : arrayOfInts ) System.out.println( i );

List<String> list = new ArrayList<String>();

list.add("foo");

list.add("bar");

for( String s : list ) System.out.println( s );

Again, we haven’t discussed arrays or the List class and special syntax in this exam‐

ple. What we’re showing here is the enhanced for loop iterating over an array of inte‐

gers and also a list of string values. In the second case, the List implements the Iterable interface and thus can be a target of the for loop.

break/continue

The Java break statement and its friend continue can also be used to cut short a loop or conditional statement by jumping out of it. A break causes Java to stop the current loop (or switch) statement and resume execution after it. In the following example, the while loop goes on endlessly until the condition() method returns true, trigger‐

ing a break statement that stops the loop and proceeds at the point marked “after while”:

while( true ) { if ( condition() ) break;

}

// after while

A continue statement causes for and while loops to move on to their next iteration by returning to the point where they check their condition. The following example prints the numbers 0 through 99, skipping number 33:

for( int i=0; i < 100; i++ ) { if ( i == 33 )

continue;

System.out.println( i );

}

The break and continue statements look like those in the C language, but Java’s forms have the additional ability to take a label as an argument and jump out multiple levels to the scope of the labeled point in the code. This usage is not very common in day-to-day Java coding, but may be important in special cases. Here is an outline:

labelOne:

while ( condition ) { ...

labelTwo:

while ( condition ) { ...

// break or continue point }

// after labelTwo }

// after labelOne

Enclosing statements, such as code blocks, conditionals, and loops, can be labeled with identifiers like labelOne and labelTwo. In this example, a break or continue without argument at the indicated position has the same effect as the earlier exam‐

ples. A break causes processing to resume at the point labeled “after labelTwo”; a con tinue immediately causes the labelTwo loop to return to its condition test.

The statement break labelTwo at the indicated point has the same effect as an ordi‐

nary break, but break labelOne breaks both levels and resumes at the point labeled

“after labelOne.” Similarly, continue labelTwo serves as a normal continue, but con tinue labelOne returns to the test of the labelOne loop. Multilevel break and con tinue statements remove the main justification for the evil goto statement in C/C++.4 There are a few Java statements we aren’t going to discuss right now. The try , catch, and finally statements are used in exception handling, as we’ll discuss in Chapter 6.

The synchronized statement in Java is used to coordinate access to statements among

multiple threads of execution; see Chapter 9 for a discussion of thread synchronization.

Unreachable statements

On a final note, we should mention that the Java compiler flags “unreachable” state‐

ments as compile-time errors. An unreachable statement is one that the compiler determines won’t be called at all. Of course, many methods may never actually be called in your code, but the compiler detects only those that it can “prove” are never called by simple checking at compile time. For example, a method with an uncondi‐

tional return statement in the middle of it causes a compile-time error, as does a method with a conditional that the compiler can tell will never be fulfilled:

if (1 < 2) {

// This branch always runs

System.out.println("1 is, in fact, less than 2");

return;

} else {

// unreachable statements, this branch never runs

System.out.println("Look at that, seems we got \"math\" wrong.");

}

Dokumen terkait