Sponsored Link •
|
Advertisement
|
Another difference between the source files of C++ and the source files of Java is that Java source is written in Unicode, a 16-bit international character set standard. If you create an ASCII source file, the Java compiler will treat the ASCII characters as if they were the equivalent Unicode characters. This will be discussed at greater length in Chapter 3.
This chapter will reveal to you the details of expressions and statements in the Java language. It discusses operators, expressions, literals, local variables, control-flow, and gives some general guidelines on various aspects of method implementation.
Expressions perform operations on data and move data around. Some expressions will be evaluated for their results, some for their side effects, some for both. An expression can have three kinds of result:
(4 * i)
i = 4
void
)
Each type in the Java programming language has literals, the way to specify
unnamed constant values for the type. For example, the boolean type has two literals,
true
and false
. Object reference types have only one literal,
null
.
Integer literals come in three forms: decimal, hexadecimal, and octal. The way an integer literal
begins indicates the base of the number. If the number begins with a 0x
or
0X
, it is hexadecimal (base 16). Some examples are: 0x5
,
0X00FF
, and 0xcafebabe
. If the number begins with only a
zero, it is an octal (base 8). Some examples are: 035
, 0777
,
0321
. If the number begins with a non-zero digit, it is decimal:
31
, 255
, 20
. If an integer literal ends in an
L
or l
, it is a long
, otherwise it is an
int
. Some examples of long integer literals are: 0XCAFEBABEL
,
035L
, 31L
. If an int
literal is assigned to a
variable of type short
or byte
, the literal is treated as if it were a
short
or byte
type so long as the literal value is within the valid
range for that type.
Floating point literals are made up of decimal digits, optionally containing a decimal point, and
optionally followed by an E
or e
and an exponent. Some examples
of floating point literals are: 3.14159
, .314159e1
,
34.26E23
. If a floating point literal ends in a F
or
f
, it is a float
, otherwise it is a double
.
Optionally, a double
floating point literal can end in D
or
d
. Some examples of float literals are: 3.14159F
and
3e5f
. The same values expressed as doubles could look like this:
3.14159
and 3e5D
.
Character literals can be any Unicode character between single quotes, such as
'A'
. In addition to providing an explicit character between the single quotes, you can
provide an octal or hex number preceded by a backslash. The octal number must be between
'\0'
and '\377'
. The hex number must have four digits. It is
preceded with either a \u
or \uu
, as in:
'\u0073'
and '\uu039d'
. There are also several character
literals represented by special escape sequences, shown in Table 3-1.
Table 3-1. Special Character Literal Escape Sequences
Literal | Meaning |
\n | line feed (\u000A ) |
\b | backspace (\u0008 ) |
\t | tab (\u0009 ) |
\f | form feed (\u000C ) |
\r | carriage return (\u000D ) |
\" | double quote (\u0022 ) |
\' | single quote (\u0027 ) |
backslash (\005C ) |
The hex version of the character literal can have either one or two u
's to facilitate
conversion of Unicode source files to ASCII and back again. All Java source files are Unicode by
definition. When you create an Java source file in ASCII, it is treated as the first 128 Unicode characters.
(The first 128 Unicode characters, \u0000
to \u007f
, map to the
7-bit ASCII character set. The second 128 Unicode characters, \u0080
to
\u00ff
, map to the ISO-Latin-1 character set.) If you include in a Unicode source file
the Greek character 'q'
(lower case theta), it will be converted into the string
'\u03b8'
in the ASCII file. When the ASCII file is converted back to Unicode, the
'\u03b8'
will be changed back into a 'q'
. If, however, in your
Unicode file you included the character literal '\u03b8'
, this would be converted
into '\uu03b8'
in the ASCII file. If the extra u
were not added,
when the ASCII file was translated back to Unicode you'd end up with a 'q'
where
you used to have a '\u03b8'
. To avoid this problem,
'\uu03b8'
is transformed into '\u03b8'
when converting
from ASCII back to Unicode.
The Unicode escape sequences are valid not only for character and string literals, they are valid anywhere in a Java source file. For example, instead of typing this into your Unicode source file:
// (Not on the CD-ROM) class Example1a { public static void main(String[] args) { int q = 25; System.out.println(q); } }You could type this into a Unicode or an ASCII source file:
// In Source Packet in file expr/ex1/Example1b.java class Example1b { public static void main(String[] args) { int \u03b8 = 25; System.out.println(\u03b8); } }If you wanted to be extremely cryptic, you could even type:
// In Source Packet in file expr/ex1/Example1c.java class Example1c { public static void main(String[] args) { \u0069\u006e\u0074\u0020\u03b8\u0020\u003d \u0020\u0032\u0035\u003b System.out.println(\u03b8); } }The
main()
method of Example1c
starts with the same
statement as the previous two examples, but with every character--including spaces and the semicolon--
replaced by the equivalent Unicode escape sequence. All three versions compile and do the same thing
when run: they declare an int
named q
, initialize it to 25, and
print out its value.
String literals, which appear between double quotes, as in "appear"
, are actually
references to instances of class String
. Strings will be described in detail in Chapter 10.
Java's binary arithmetic operators--addition: +
, subtraction: -
,
multiplication: *
, division: /
, and remainder: %--
operate on any numerical type. The %
operator, which is called
modulo in C++, is called remainder in Java. It performs the same operation:
calculating the remainder of a division. You can also use the +
operator to concatenate
strings. String concatenation will be described in Chapter 10.
[bv: may want to add more details of IEEE754 math]
Java also has unary +
and -
, which allow you to indicate a
literal number is positive or negative, as in -3
or +4.0
. If you
don't specify a unary +
or -
, a literal number is interpreted as
positive. The unary - can also be used to negate a variable, as in: negativeVal = -
val
.
Java also has increment: ++
, and decrement: --
, operators.
These operators can be placed before or after the variable expression they modify. If they are placed before
(a prefix operator), they modify the variable expression before its value is used. If they are
placed after (a postfix operator), they modify the variable expression after its value is used.
Java's relational and equality operators are: greater than: <
, less than:
>
, greater than or equal to: >=
, less than or equal to:
<=
, equal to: ==
, and not equal to: !=
. All
yield a boolean result. The unary !
operator inverts boolean value.
The conditional operators, conditional-AND: &&
, and conditional-OR:
||
, take boolean operands and yield a boolean result. Expressions built from these
operators only evaluate as far as needed to determine the result. For example, assume
salt()
, pepper()
, toBe()
,
and
notToBe()
are methods that return a boolean. If
salt()
evaluates to false
in the expression:(salt()
&& pepper())
, then pepper()
is never evaluated. Likewise, if
toBe()
evaluates to true
in the expression:(toBe()
|| notToBe())
, then notToBe()
is never evaluated. Conditional-
AND and conditional-OR are also called logical-AND and logical-OR.
Java has several operators that perform operations on individual bits. The binary bitwise
operators are: bitwise AND: &
, bitwise inclusive OR: |
, and bitwise
exclusive OR (or bitwise XOR): ^
. The unary bitwise complement operator,
~
, inverts each bit in its operand. There are also three shift operators: shift left:
<<
, shift right: >>
, and unsigned shift right:
>>>
. The shift operators shift the integer value on the left of the operator
by the amount specified by the integer value on the right. Shift left and unsigned shift right fill with
zeroes as they shift. Shift right fills with the highest bit (the sign bit) of the left hand value as it shifts.
The conditional operator, ? :
, is a shorthand for an if-then-
else
construction that does one of two different things depending upon the result of a boolean
expression. The expression:
variable = booleanExpr ? value1 : value2;has the same effect as:
if (booleanExpr) { variable = value1; } else { variable = value2; }The conditional operator is also sometimes called the ternary operator or the question- colon operator.
In addition to the basic assignment operator, =
, Java includes many shorthand
assignment operators that allow you to write expressions such as j = j + 1
in
shorthand as j += 1
. The main difference between these two approaches is that in
the shorthand version, the variable expression j
is evaluated only once. In the
longhand version, the variable expression j
is evaluated twice. This difference matters
in cases where the variable expression has a side effect, as in someArray[i++]
. All
the shorthand assignment operators are listed in Table 3-2. [bv: must add in about one guy
needing to be assignable to the other and that being the type of the
expression.]
Operator precedence determines which parts of an expression are evaluated before the other parts. For example, the expression
2 + 2 * 7evaluates to 16, not 28, because the * operator has a higher precedence than the + operator. Thus the 2 * 7 part of the expression is evaluated before the 2 + 2 part. If you wish, you can use parentheses in expressions to clarify evaluation order or to override precedence. For example, if you really wanted the result of the expression above to be 28, you could write the expression like this:
(2 + 2) * 7
Table 3-2 shows all Java's operators and their precedence. The postfix operators, shown at the top of the table, have the highest precedence. The assignment operators, shown at the bottom, have the lowest precedence.
Table 3-2. Operator precedence
Operator Type | Operator |
Postfix operators | [] . ( params) expr++ expr-- |
Unary operators | ++expr --expr +expr -expr ~ ! |
Creation or cast | new ( type) expr |
Multiplicative | * / % |
Additive | + - |
Shift | << >> >>> |
Relational | < > >= <= instanceof |
Equality | == != |
Bitwise AND | & |
Bitwise exlusive OR | ^ |
Bitwise inclusive OR | | |
Conditional-AND | && |
Conditional-OR | || |
Conditional | ?: |
Assignment | = += -= *= /= %= >>= <<= >>>= &= ^= |= |
When multiple operators of the same precedence appear side by side in an expression, the
associativity of the operators determines the order of evaluation. In Java, all binary operators
except the assignment operators are left-associative. Assignment operators are right-
associative. For example, i + j - k
is evaluated as (i + j) -
k
. i = j = k
is evaluated as i = (j = k)
.
Operator precedence is part of the Java language. You needn't be afraid to use it. But on the other hand, if you find yourself attempting to show off your knowledge of precedence, consider using parentheses to clarify what operators are operating upon what expressions. When you do rely on precedence, make sure you are as smart as you think you are. Many bugs arise from mistaken assumptions about precedence. The best is approach is somewhere in the middle. If you use too many parentheses, or not enough, the code can be difficult to understand. You should choose the form that you feel maximizes your code's readability. Remember, it's not a contest to see who can write the most cryptic code, but who can write the most lucid code.
One approach to writing complex expressions is to separate them out into several stages, placing intermediate values into variables with descriptive names. Using well-named variables to hold intermediate values of long computation expressions can help others understand what is going on. As a bonus, a programmer can check the value of intermediate results in a debugger by inspecting the intermediate variables.
After taking into account precedence and parentheses, Java guarantees that expressions will be
evaluated left to right. For example, to evaluate eat() + drink() -
beMerry()
, Java will first evaluate eat()
, then
drink(
), then perform the addition, then evaluate beMerry()
,
and finally perform the subtraction. eat()
is evaluated before
drink()
, because eat()
is to the left of
drink()
, and expressions are evaluated left to right. This guarantee is important
because the invocations of eat()
and drink()
may have side
effects that would differ if they were invoked in the opposite order.
In general, every operand to an operator is evaluated before the operator is evaluated. Three
exceptions are the conditional-AND: &&
, the conditional-OR: ||
,
and the conditional operator: ?:
. The right hand side of conditional-AND and
conditional-OR expressions won't be evaluated if the left hand side determines the result. For example, if
the left hand side of a conditional-AND expression evaluates to false
, the result of
the expression will definitely be false
, so the right hand side is not evaluated.
Likewise, if the left hand side of an conditional-OR expression evaluates to true
, the
result of the expression will definitely be true
, so the right hand side is not evaluated.
In the case of a conditional expression, the boolean-expression is evaluated first. Depending upon the
result of this evaluation, only one of the other expressions will be evaluated. For example, in the
expression isHungry() ? eat() : beMerry()
,
isHungry()
will be evaluated first. If it evaluates to true
,
eat()
will be evaluated and beMerry()
will not. Otherwise,
beMerry()
will be evaluated and eat()
will not.
A Java method body is a series of zero or more statements. In the Java programming language, statements are the fundamental unit of execution. All statements except blocks are terminated by a semicolon. Blocks are denoted by open and close curly braces. Statements are executed for their effects; they do not have values. There are many different kinds of statements in Java:
if
and if-else
statements
while
statement
do-while
statement
for
statement
break
statement
continue
statement
return
statement
switch
statement
throw
statement
try
statement
synchronized
statement
Most of these statement types will be described below. A few will be described in later chapters.[bv: am I missing anything that was added in JDK 1.1 or 1.2?]
A block is a series of zero or more statements between a matching set of open and close curly braces.
The bodies of methods and switch
statements are blocks.
The bodies of if
,
for
, while
, and do-while
statements may also be blocks. Also, you can also simply create a new block
inside another block by enclosing code within curly braces. A block contained within another block is
itself a statement of the outer block. Blocks that contain no statements are called empty.
[bv: need to add final here.]
Declaration statements establish a name, type, and possibly an initial value for a new local variable. Local variables in Java can be declared anywhere in a method. A local variable need not be initialized where it is declared, but it must be initialized before it is used. If it isn't, the source file won't compile. For example, the following snippet of code won't compile:
// In Source Packet in file expr/ex2/Example2a.java class Example2a { public static void main(String[] args) { // THIS WON'T COMPILE. int i; if (i < 100) { //... } } }This code won't compile because
i
is used before it is initialized. To fix this problem,
you could initialize i
when you declare it:
// In Source Packet in file expr/ex2/Example2b.java class Example2b { public static void main(String[] args) { // This compiles fine. int i = 5; if (i < 100) { //... } } }Alternatively, you could assign a value to
i
after you declare it, but before you use it:
// In Source Packet in file expr/ex2/Example2c.java class Example2c { public static void main(String[] args) { // This compiles fine. int i; i = 5; if (i < 100) { //... } } }
The "i
" in "int i
" and the "i = 5
" in
"int i = 5
" are called declarators. As in C++, you can place
multiple local variable declarators, separated by commas, into the same local variable declaration
statement, as in:
int i, j = 5, k;
The scope of a local variable extends from the point where the local variable is declared to the end of
the block in which it is declared. No other local variable of the same name can be declared within a local
variable's scope. For example, the following won't compile, because a second local variable
i
is declared within the scope of a first local variable i
:
// In Source Packet in file expr/ex2/Example2d.java class Example2d { public static void main(String[] args) { // THIS WON'T COMPILE. int i = 4; if (i < 10) { int i = 3; // Can't use name i again. //... } } }
;
[note to editors: the above semicolon is intended to be the entire text of this section. If I were to explain the semicolon in a sentence, it would be: "The empty statement, represented by a semicolon and nothing else, does nothing." The lone semicolon above is an empty (English) statement about the empty (Java) statement. All the programmers who reviewed this chapter "got it."]
Expression statements are valid Java expressions that are terminated by a semicolon. Unlike C and C++, not all kinds of valid expressions can be expression statements. In Java, there are four kinds of expression statements:
a += 5
, b *= 7
, or
c = 3
++a
, --b
,
c++
, d--
new CoffeeCup
void
) yield a value upon evaluation. When an expression is used as an expression
statement, the end value of the expression is discarded. Such expressions are evaluated simply for their
side effects, as in:
++i;The full details of expressions will be expressed later in this chapter.
if
and if-else
StatementsThe simplest control flow construct in Java is the if
statement, which can have an
optional else
clause. Here is the format without the else
clause:
if (boolean-expression) statementThe
statement
can be a simple statement terminated by a semicolon or
a block enclosed in curly braces. Alternatively, you can add an else
:
if (boolean-expression) statement1 else statement2
As in C++, an else
in Java matches the closest if
.
Note that Java's if
statement differs from that of C++ in that the expression
contained in the parentheses must be boolean
. In Java, if i
is an
int
, then if (i)
won't compile. You must say if (i
!= 0)
. This is also true of the expressions in the while
,
do
, and for
loops. This feature of Java enables the compiler to
catch accidental use of =
instead of ==
inside an expression. For
example, if you accidentally type i = 3
instead of i == 3
in an
if
expression, it won't compile:
// In Source Packet in file expr/ex2/Example2e.java class Example2e { public static void main(String[] args) { int i = 4; // In Java, THIS WON'T COMPILE, because the resulting // value of the expression isn't boolean. if (i = 3) { //... } } }
while
and do-while
StatementsThe while statement looks like this:
while (boolean-expression) statementJava's
while
loop behaves like C++'s while
loop (the difference
between the two is that Java's expression must be boolean.) The boolean-
expression
is evaluated first. If the expression is true
,
statement
is executed. This process is repeated until the expression
evaluates to false
(or a break
, continue
,
or return
is executed, or an exception is thrown).
The do-while
statement differs from the while
in that the
statement
is always executed at least once. In a
while
loop, if the boolean-expression
evaluates to false
the first time, statement
is
never executed. Here is what a do-while
looks like:
do statement while (boolean-expression);
for
StatementThe for
loop is used to iterate through a sequence of values. It takes the same
form as in C++:
for (init-expr; boolean-expr; incr-expr) statement
The init-expr
is an initialization expression, such as i
= 0
. You can optionally initialize multiple variables in init-
expr
by separating each initialization expression by a comma, as in i = 0,
j = 0
. You can even declare a new variable in the init-
expr
:
for (int i = 0; i < 10; ++i) { //... }
The scope of this variable i
is only within the
statement
portion of the for
itself (in this
example, a block). This contrasts with C++, which also allows you to declare a variable in the
init-expr
portion of a for
loop, but that variable
has a scope as if it were declared just above the for
loop. In Java, the variable has a
scope as if it were declared inside the statement
of the
for
loop.
The incr-expr
is usually an increment expression such as
++i
. Like the init-expr
, it can contain multiple
statements separated by commas, as in: ++i, ++j
.
The for
loop executes by first executing init-
expr
, then evaluating boolean-expr
. If
boolean-expr
evaluates to true
,
statement
is executed. After
statement
is executed, incr-expr
is executed
and boolean-expr
is checked again. The process of
boolean-expr
check, statement
execution, incr-expr
execution repeats until
boolean-expr
evaluates to false
(or a
break
, continue
, or return
is executed, or
an exception is thrown).
Each of the three components in a for
loop's parentheses are optional. If you
leave out the boolean-expr
, it is assumed to be true
. Thus, the
customary way to write an infinite loop is:
// a "forever" loop for (;;) { //... }
switch
StatementThe switch
statement gives an alternative to a cascade of if-
else
constructs:
// In Source Packet in file expr/ex3/Example3a.java class Example3a { public static void main(String[] args) { int i = (int) (Math.random() * 5.0); if (i == 0) { System.out.println("Zero"); } else if (i == 1) { System.out.println("One"); } else if (i == 2 || i == 3) { System.out.println("Two or Three"); } else { System.out.println("Other"); } } }The equivalent switch statement is:
// In Source Packet in file expr/ex3/Example3b.java class Example3b { public static void main(String[] args) { int i = (int) (Math.random() * 5.0); switch (i) { case 0: System.out.println("Zero"); break; case 1: System.out.println("One"); break; case 2: case 3: System.out.println("Two or Three"); break; default: System.out.println("Other"); break; } } }A
switch
's expression (i
in this example) must be an
int
. The case labels must be compile-time constants. They can either be literals, as
shown here, or static
final
fields
of some class. A break
statement is used in a switch
to indicate
the processing for that case
is done. You need not have a break
for every case
. You can "fall through" to the code for the next case label, as was done
by case 2
in this example.
break
and continue
StatementsYou can use break
to exit while
, do-
while
, for
, and switch
statements. When a break is
executed, the execution continues just after the end of the current simple statement or block.
[bv: actually, I think this statement is too simple. break hops to the end
of the current while, do-while, or switch block, right?]
You can use continue
inside a while
, do-
while
, and for
loop. When a continue
is executed,
the rest of the loop's body is skipped and the boolean-expression
is again evaluated.
You can optionally use a label to indicate which loop or switch
you want a
break
to operate on, or which loop you want a or continue
to
operate on. This enables you to easily break out of nested loops or switch
statements,
or to continue nested loops. To do so you must label the beginning of the loop or switch statement:
label: statementHere's an example:
// In Source Packet in file expr/ex4/Example4.java class Example4 { public static void main(String[] args) { dance: for (int i = 0; i < 10; ++i) { System.out.println(i + ": Swing your partner..."); for (int j = 0; j < 10; ++j) { System.out.println(j + ": Round and round..."); if (i == 5 && j == 5) { break dance; } } } // Execution continues here after break dance is encountered. System.out.println("Now, twirl on your back."); } }
When the break
dance
statement is executed, the
for
loop labeled dance
(the outer loop) is exited.
return
StatementThe return
statement returns from a method, potentially returning a value. If the
method is declared as void
, you must use a simple return
statement:
return;Otherwise, you must indicate a return value with a type that matches the return type of the method. For example, a method declared with a boolean return type could have the following statement:
return true;
A handful of other statements will be covered in later chapters. Chapter 11 will describe the
throw
and try
statements. Chapter 13 will describe the
synchronized
statement.
With one exception, statements in a Java program must be reachable. There must be at
least one path of execution that will "reach" each statement. For example, the following code won't
compile, because the ++i
is unreachable:
// In Source Packet in file expr/ex5/Example5a.java class Example5a { public static void main(String[] args) { // THIS WON'T COMPILE int i = 0; while (false) { ++i; } } }The exception to the reachability rule is the
if
statement. The following code does
compile:
// In Source Packet in file expr/ex5/Example5b.java class Example5b { public static void main(String[] args) { // This compiles fine. int i = 0; if (false) { ++i; } } }The reason the
if
statement is special is so it can be used for conditional compilation.
(Because Java doesn't have a preprocessor like C++, it can't use C++'s method for conditional
compilation.) The above code would compile, but the if (false) { ++i; }
code
would not appear in the class file.
Sponsored Links
|