This book has been using broad terms to discuss the significance of Java as a programming language. It now digs deeper into Java to discuss the building blocks for writing programs. Those of you who are familiar with other languages such as C or C++ will find many familiar constructs in Java. However, you may have to "unlearn" some programming habits.
This chapter explains the fundamentals of Java programming language from broad concepts and logical program flow to specific keywords.
The Java programming language has a term for its building blocks, or basic elements: tokens. Becoming familiar with these words and concepts is a prerequisite to programming in Java.
Chapter 4, "The Java Language: A Primer," explains the series of events surrounding compiling and running an application. This chapter explains the logic of the javac compiler.
At compile time, the javac compiler takes the code and pulls out specific information, called a token, for further processing.
First, javac takes out escape sequences from the raw byte codes. It determines if the escape sequences are line terminators or input characters. Next, javac takes out whitespace that is not internal to a string, including the ASCII space character, carriage return, line feed, and horizontal tab. Then the compiler gets down to the business of pulling out tokens.
There are several types of tokens: identifiers, keywords, literals, operators, separators, and comments.
The javac compiler needs to know the names of items in the program. For example, it must recognize the names of variables, methods, and component elements of classes. Identifiers are either Java reserved words or titles given to variables, classes, and methods. Reserved words are names that may be used only by Java. Using these words in any other way will cause a compile error.
Identifiers can be named anything as long as they begin with an alphabet character, a dollar sign, or an underscore. However, I do not recommend that you use the dollar sign or begin an identifier name with an underscore because these symbols are used by the Java libraries. This will help with debugging. Do use the underscore to connect words to make an identifier meaningful, as in this_book. As with all good programming, the more descriptive the identifier, the better.
Java will often give a programmer just enough rope to get into interesting knots. For example, the javac compiler will accept uppercase or mixed-case words the same way it does keywords.
Java identifiers are case sensitive. For example, char is a reserved word in Java. This means that it is possible to assign a variable the identifier Char, chAR, or variations thereof. However, this makes debugging difficult. You'll learn more on reserved words in the next section.
Keywords are reserved words, which means that they cannot be used in any way other than how Java intends for them to be used. Keywords are, therefore, special tokens. They are always lowercase.
Java keywords are used as application flow controls, declarations,
class identifiers, and expressions. Table 6.1 lists all the reserved
keywords in Java. Keywords that pertain to the fundamentals of
Java are explained in the appropriate section of this chapter.
Data Declaration Keywordsboolean Loop Keywordsbreak Conditional Keywordscase Exception Keywordscatch Structure Keywordsabstract Modifier and Access Keywordsfinal Miscellaneous Keywordsfalse |
The following keywords are reserved and are not being used in Java 1.0; they may be used in future releases:
byvalue
cast
const
future
generic
goto
inner
operator
outer
rest
var
Data is represented by literals in Java. Similar to literals in other programming languages, Java literals are based on character and number representations. The types of literals are integer, floating-point, boolean, character, and string.
Every variable consists of a literal and a data type. The difference between the two is that literals are entered explicitly into the code. Data types are information about the literals, such as how much room will be reserved in memory for that variable, as well as the possible value ranges for the variable. (Data types are discussed in the section "Using Data Types.")
Integers are whole numbers, such as 1 and 5280. Integer literals can be decimal (base 10), octal (base 8), or hexadecimal (base 16).
Decimals can be positive, zero, or negative. Decimal literals cannot start with 0, as in 01234. After the beginning number, a decimal literal can consist of the numbers 0-9. Numbers beginning with 0 are reserved for octal and hexadecimal literals. Therefore, when using decimals, do not right-justify with leading zeros. The upper limit of a positive decimal integer is 231-1, or 2,147,483,647. The lower limit is -2,147,483,648 or -231.
Octal literals start with 0 and can be followed by any number 0-7. They can be positive, zero, or negative. The maximum value of an octal literal is 017777777777, which is equivalent to 231-1.
Hexadecimal integer literals start with 0x or 0X, followed by one or more hexadecimal digits. Letters A-F used in a hexadecimal integer can be uppercase or lowercase. Hexadecimal integers can be positive, zero, or negative. The upper limit of a positive hexadecimal literal is 0x7fffffff, which is, once again, equivalent to 231-1. The values available are 1-9, A-F, and a-f.
A compile-time error will occur if any of these values is exceeded.
A floating-point literal represents a number that has a decimal point in it, such as 3.7. Java standards specify that floating-point numbers must follow the industry standard specification as written in IEEE-754.
Single-precision floating-point numbers consist of a 32-bit space and are designated by uppercase or lowercase f. Double-precision numbers are allotted a 64-bit space and are designated by uppercase or lowercase d. Double-precision floating-point numbers are the default. Therefore, 3.7 is a double-precision floating-point number, and 3.7f is a single-precision floating-point number.
How do you know whether to use a single- or double-precision floating-point number? It depends on the size of the number. If there is any possibility that a number could grow out of range or need to be of greater precision than single, declare it a double.
Compile-time errors will occur if a nonzero floating-point literal is too large or small.
The largest magnitude single-precision floating-point literal is ±3.40282347e+38f, and the smallest is ±1.40239846e-45f. The largest double-precision floating-point number is 1.79769313486231570e+308, and 4.94065645841246544e-324 is the smallest floating-point number.
Note |
Floating-point literals also can be expressed using exponents or scientific notation, as shown in the preceding paragraph. Uppercase or lowercase e is used to denote the exponent portion of a floating-point number. An example of this is 2.1e3f; this is a single-precision floating-point number representing 2100. |
A boolean literal is either of the words true or false. Unlike other programming languages, no numeric value such as 0 or 1 is assigned. Therefore, the value of a boolean literal is, literally, true or false. Booleans are used extensively in program control flow logic.
Programmers often use a single character as a value. In Java, this is represented by character literals. The value of a character literal is enclosed by single quotes. An example is 'y'.
Assigning a value to a character literal gets more interesting
if the value must be a single quote, a backslash, or other nonprintable
characters. A backslash (\)
is used to designate certain nonprintable characters or characters
that are properly part of the command. For example, assign the
value '\'' to characterize
the single quote. Table 6.2 shows some examples of assigning values
to character literals.
Output | ||
Any character | 'y' | y |
Backspace (BS) | '\b' | Backspace |
Horizontal tab (HT) | '\t' | Tab |
Linefeed (LF) | '\n' | Linefeed |
Formfeed (FF) | '\f' | Form feed |
Carriage return (CR) | '\r' | Carriage return |
Double quote | '\"' | " |
Single quote | '\'' | ' |
Backslash | '\\' | \ |
Octal bit pattern | '\ddd' | Octal value of ddd |
Hex bit pattern | '\xdd' | Hex value of dd |
Unicode character | '\udddd' | Actual Unicode character of dddd |
Compile-time errors occur if anything other than a single quote follows the value designator or the character after '\' is anything but b, t, n, f, r, ", ', \, 0, 1, 2, 4, 5, 6, or 7.
String literals are a sequence of characters enclosed in double quotes, such as "This is a string literal". This could also be "Hello World" or even "" for a null character string. The javac compiler does not strip out whitespace from within string literals.
String literals can be concatenated. For example, if one string contained "This is the beginning" and another string contained " of a beautiful relationship", they could be concatenated together. The representation would be "This is the beginning" + " of a beautiful relationship". (It is not necessary to have spaces on either side of the plus sign.)
A string literal cannot span more than one line; those that do so can be broken up at declaration time and then concatenated at use time. This also makes programs more readable and strings less likely to be mistaken for comments.
As with the character literal, the backslash is used to denote symbols that otherwise would not work. The double quote is represented by the string "\"".
Java uses the following separators: (), {}, [], ;, ,, and ..
The compiler uses these separators to divide the code into segments. They can also force arithmetic precedence evaluation within an expression. (You will learn more about precedence in the section "Using Operators in Expressions.") Separators are also useful as visual and logical locators for programmers.
Note the use of separators in the following code fragment:
if (location < 10) {
Here, the parentheses indicate to the compiler the boolean expression to be evaluated, which is whether the value of location is less than 10. The trailing { indicates the start of a block of code that will be executed if the boolean expression evaluates to true.
Operators are the symbols used for arithmetic and logical operations. Arithmetic symbols define operations that apply to numbers (for example, 2 + 3). Operators, except the plus sign (+), are used only for arithmetic calculations. The + operator can be used with strings as well, as shown in the previous example of string literal concatenation. Tables 6.3 through 6.7 list all the Java operators.
+ | Addition | g + h |
- | Subtraction | g - h |
* | Multiplication | g * h |
/ | Division | g / h |
% | Modulus | g % h |
= | Assign value | a = 7 | a = 7 |
+= | Add to current variable | g += h | g = g + h |
-= | Subtract from current | g -= h | g = g - h |
*= | Multiply current | g *= h | g = g * h |
/= | Divide current | g /= h | g = g / h |
%= | Modulus current | g %= h | g = g % h |
++ | Increment by 1 | g++ or ++g | g = g + 1 |
-- | Decrement by 1 | g-- or --g | g = g -1 |
== | Equal | g == h | Is g equal to h? |
!= | Not equal | g != h | Is g not equal to h? |
< | Less than | g < h | Is g less than h? |
> | Greater than | g > h | Is g greater than h? |
<= | Less than or equal | g <= h | Is g less than or equal to h? |
>= | Greater than or equal | g >= h | Is g greater than or equal to h? |
& | Bitwise AND |
| | Bitwise OR |
^ | Bitwise XOR |
<< | Left shift |
>> | Right shift |
>>> | Zero fill right shift |
~ | Bitwise complement |
<<= | Left shift assignment |
>>= | Right shift assignment |
>>>= | Zero fill right shift assignment |
x&=y | AND assignment |
x|=y | OR assignment |
x^=y | NOT assignment |
Some programmers believe that if something is difficult to code, it should be difficult to maintain. There is a special place in the cosmos for them; let's hope that it's not on your project! Of course, comments should be used throughout code to explain the programmer's rationale. Comments also can be used to block out certain code sections for testing purposes.
Comments have an initial indicator (usually text) and an end indicator. Sometimes comments are used to separate logic, in which case there may be no text.
Table 6.8 illustrates three ways to indicate comments in Java.
/* | text | */ |
/** | text | */ |
// | text | (everything to the end of the line is ignored by the compiler) |
The /* comment is familiar to C programmers. The /** comment is typically used for machine-generated comments. The third style is familiar to C++ programmers.
The following are examples of comments:
/* this is an example of a comment */
/** this is another example of a comment. I can
go to multiple lines and the compiler will
look at this as a comment until I do the proper
end comment as in */
// with this method I can't go to multiple lines unless
// I start each line with the comment marker.
Comments can be used anywhere in code without affecting the code's execution or logic. Be sure to terminate the comment, or the compiler will get confused.
Warning |
If you forget to terminate a comment, you will see strange results when you try to compile or run the program. Basically, what happens is that the compiler interprets everything up to the next end-comment indicator as part of the comment, which can cut out entire sections of your program. |
Tokens are the building blocks, the most basic parts of Java. An understanding of them is essential to successful programming in Java. The following sections give examples of the uses of tokens in Java.
A variable is something that changes, or varies. In Java, a variable stores data. Data types define the kind of data that can be stored in a variable and the limits of the data.
An example of the use of a data type is
char my_letter;
This example demonstrates the two essential parts of a variable: the type (char) and the identifier (my_letter). The type notifies the compiler that the variable my_letter will be of type char. Recall that char is a reserved keyword, and data types are always designated by one of the reserved keywords. The variable name my_letter at this point has the value of null, which is the default value of type char. Finally, the semicolon tells the compiler that the defining of the variable is finished.
Another example of defining data types follows:
int no_of_albums, b, YellowRose, chair;
This code example has four variables: no_of_albums, b, YellowRose, and chair. Each of them is of type int (a reserved keyword). You can enter multiple variables of the same type on the same line when separated by a comma. The default value for integers is 0. Once again, the semicolon ends the definition.
There are two major data types in Java: reference types and primitive types.
Data types can be stored in variables, passed as arguments, returned as values, and operated on.
Note |
Remember, the definition or typing of a variable does not assign a value to the variable; it simply identifies the possible values of that variable. |
Primitive data types can have only one value at a time. They do not reference other data or indicate sequence in a group of data. They are primitive in that they are the simplest built-in forms of data in Java. All other data types are made up of combinations of primitive data types. The primitive data type keywords are shown in Table 6.9.
Boolean
char byte short int long float double |
There are four integer data types: byte,
short, int,
and long. Each can handle
different ranges of numbers, as summarized in Table 6.10.
byte | 8 bits | -128 | 127 |
short | 16 bits | -32768 | 32767 |
int | 32 bits | -2147483648 | 2147483647 |
long | 64 bits | -9223372036854775808 | 9223372036854775807 |
Java does some interesting things to integer values during operations. For example, a variable SmallNumber of type byte and a variable LargeNumber of type int are added together. The variables both automatically become type int, or 32 bits in length. SmallNumber widens to 32 bits for the operation. (The exception is if the result is boolean, in which case no variables are expanded.) So whenever two integers of varying length are operated on, the smaller of the integers becomes the same length in memory as the larger.
The only case in which ArithmeticException is called is when there is an attempt to divide by zero, so test for zero before attempting a divide operation.
In the section titled "Conditional Expressions" you will find examples of how to check numerical values.
Other interesting things happen in Java when the value of an integer exceeds its defined range. ArithmeticException will not occur. Instead, the value "wraps" around to the other end of the numeric range for that type. Here is an example:
class IntWrap {
public static void main (String args[])
{
byte StartNumber = 120;
byte SmallNumber;
int LargeNumber;
SmallNumber = StartNumber;
for (LargeNumber=0; LargeNumber < 16; LargeNumber++) {
System.out.println("StartNumber(" + StartNumber + ") + LargeNumber (" +
LargeNumber + ") = " + SmallNumber);
SmallNumber++;
}
}
}
After you save the source code in a file called IntWrap.java, you can compile the source code by typing the following at the command line:
javac IntWrap.java
To run the program, type this:
java IntWrap
The output from the program should be as follows:
StartNumber(120) + LargeNumber (0) = 120
StartNumber(120) + LargeNumber (1) = 121
StartNumber(120) + LargeNumber (2) = 122
StartNumber(120) + LargeNumber (3) = 123
StartNumber(120) + LargeNumber (4) = 124
StartNumber(120) + LargeNumber (5) = 125
StartNumber(120) + LargeNumber (6) = 126
StartNumber(120) + LargeNumber (7) = 127
StartNumber(120) + LargeNumber (8) = -128
StartNumber(120) + LargeNumber (9) = -127
StartNumber(120) + LargeNumber (10) = -126
StartNumber(120) + LargeNumber (11) = -125
StartNumber(120) + LargeNumber (12) = -124
StartNumber(120) + LargeNumber (13) = -123
StartNumber(120) + LargeNumber (14) = -122
StartNumber(120) + LargeNumber (15) = -121
Type char is really a 16-bit unsigned integer that represents a Unicode value. In other words, it is possible to determine Unicode characters and escape codes by using a numeric representation. Remember, each printable and nonprintable character has a Unicode value. Because Java stores all characters as Unicode, it can be used with virtually any written language in the world. This is a prime strength for Java. Unfortunately, at this time there is little support for internationalization in the way of functions or methods to, for example, print dates, monetary denominations, time, or numbers according to locale.
Note |
Unicode is an international character set. By following the Unicode standard, Java can easily be used to print text in many different languages. |
Type float designates that the variable is a single-precision, 32-bit, floating-point number. Type double is a double-precision, 64-bit, floating-point number. Here are examples of declaring floating-point variables:
float SquareFootage;
double GrossNationalProduct;
Type boolean can only have a value of true or false. Internal to Java, a boolean value is a 1-bit logical quantity.
Other programming languages have boolean values of 0 for false and 1 for true. You can get Java to mimic this behavior if necessary: As in the C programming language, x!=0 will convert the integer x to a boolean in which 0 equals false and anything else equals true. On the other hand, y?1:0 will convert the boolean variable y to 0 for false and 1 for true.
It is best to think of booleans in a new light if your "native" language is C or C++. It is a distinct data type best used to indicate success or failure.
Primitive data types are the lowest-level objects in Java. Think of them as actual nongrouped pieces of data, such as numbers and single characters. Primitive data types can be only one value at a time in a specific application. Java initializes all primitive data types to default values if an initial value is not explicitly specified by the programmer. Integer and floating-point variables are initialized to 0. The char data type is initialized to null, and boolean data types are set to false.
There are situations in which variables must be logically grouped together for manipulation, possibly because they are going to be accessed in sequence or identifiers must point to dynamically allocated objects. These are called reference data types.
Recall that a primitive data type contains the actual value of a typed variable; a reference data type contains the address of a value rather than the value itself. The advantage is that reference data types can contain addresses that point to a collection of other data types. Those data types can themselves be of primitive or reference type.
There are three types of reference variables: array, class, and interface. Class and interface data types are covered in Chapter 7, "Building Objects." |
Arrays are single- or multidimensional groups of variables. Think of going grocery shopping with a partner who is as excited about it as you are. You say, "Honey, what is the third thing on the list?" (At my house, the reply is "I would tell you if I could read your handwriting." This is always good for a little aisle discussion.) The grocery list acts as the array. Each item on the list is an element, the smallest part of an array. It is referenced by its place in the array, as in the third item on the grocery list.
Elements can be of a primitive type, as in float, char, or int. Elements can also be a class or interface type. Arrays can consist of other arrays. Arrays can be a powerful and flexible Java tool if used appropriately.
You can declare an array without allocating it. In other words, the variable itself is created, but no space is allocated in memory for array objects until the array is initialized or values are assigned to the elements of the array. Arrays are generally initialized with the new command, which creates a new instance of a reference data type. It is conceptually similar to the malloc command in C or new in C++. In Java, declaring a reference type does not automatically create space for that type; it merely creates space for the address that points to that type.
The following code fragment shows the declaration of an array, its creation, and the assignment of values to an element of an array:
class Array {
public static void main (String args[])
{
int LISTSIZE = 5;
String[] ShoppingList;
int i = LISTSIZE;
// create array
ShoppingList = new String[LISTSIZE];
// initialize array
ShoppingList[0] = "carrots";
ShoppingList[1] = "tofu";
ShoppingList[2] = "rice milk";
ShoppingList[3] = "onions";
ShoppingList[4] = "pasta noodles";
for (i=0; i < LISTSIZE; i++) {
System.out.println(ShoppingList[i]);
}
}
}
After you save the source code in a file called Array.java, you can compile the source code by typing the following at the command line:
javac Array.java
To run the program, type this:
java Array
The output from the program should be as follows:
carrots
tofu
rice milk
onions
pasta noodles
One-dimensional arrays are like the grocery list in the previous example. There is only one way to reference the items: by using a subscript to indicate which element number to access. The number used to reference the specific element of an array is called the component. This concept is represented in Java as
char ShowArray[] = new char[5];
In this example, ShowArray is the name of the array. The [5] declares that there are five elements in the array. All elements are of type char. The elements are referenced by ShowArray[0] to ShowArray[4]. In Java, the subscripts always start at zero and go to the length of the array minus 1. The new command initializes the array to null characters. Memory space is allocated to the array here so that values can be assigned to the array. Reference to elements in the array is by subscript or component. Therefore, ShowArray[1] = 'j' assigns the value of j to the second component in the array.
All the elements of an array must be of the same type.
Note |
In theory, all Java arrays are one dimensional. In fact, an array of arrays can be defined in Java, functioning as a two-dimensional array. (This characteristic of Java is noted here because the technical specification of Java states that all arrays are one dimensional.) |
Two-dimensional arrays need two reference points because the data is stored in a grid of rows and columns. Think of a two-dimensional array as similar to the cells in a spreadsheet. An element is referenced by the row and column number of its location, as in [3][4]. This means that the current element being manipulated is in row 4, column 5 of an array. (Remember that arrays start at zero!)
The main reasons that code is created are to manipulate, display, and store data. Expressions, or formulas, are Java's way of performing a computation. Operators are used in expressions to do the work of the program and to operate on variables. Expressions in Java are similar to, although a subset of, C and C++.
Variables must be declared before they are used. An error will occur if you try to operate on an undeclared variable.
The expression book = 4 uses the operator = to assign the value 4 to variable book. It is necessary to understand what Java expects of its operators and the correct syntax of its expressions to get anything accomplished in this language. So get ready to express yourself in Java!
Every expression results in a value. The operators tell the javac compiler how to manipulate the variables and other data to give the appropriate result.
An operator that manipulates a single value is called a unary operator. Binary operators act on two values.
The unary operation is useful to modify variables "in place."
(C and C++ programmers may be
familiar with this concept, but to COBOL and BASIC programmers
it may be new.) That is, a
variable can be operated on in some instances without an intermediate
variable and then reassigned to the original variable. Whew! This
operation is easy in Java. Table 6.11 summarizes the unary operators.
- | Unary negation |
~ | Bitwise complement |
++ | Increment |
-- | Decrement |
! | Not |
Increment and decrement operators are operators that actually change the value of the original variable. For example, after g++ is executed, the value of the variable g will have been incremented by one. Likewise, the decrement will actually decrement variable g by one. The other unary operators do not change the original variable but enable the coder to work with a "temporary" variable. For example, after !g is executed, the value of g will remain unchanged.
Incrementing increases the value by one, as in
GoUp++; //GoUp is now incremented by 1
Decrementing decreases the value by one, as in
GoDown--; //GoDown is now decremented by 1
The bitwise complement reverses the bit structure of a variable. It changes 1 to 0 and 0 to 1. Unary negation multiplies the variable by -1, in effect reversing the sign of an integer from positive to negative and negative to positive. However, the sign reversal is only in effect during the operation. The variable itself is not affected.
The following example demonstrates unary negation:
OppositeMe = -15;
CheckMe = -OppositeMe + 1; //CheckMe has the value of 16
System.out.println(OppositeMe); //OppositeMe still has the value of -15
An assignment operator stores a specific value in the memory area allocated to a variable. For example, in the expression g += 5;, 5 is added to the original value of variable g, and the old value is replaced. Here are more examples:
g = 5; // assigns to g the value 5
g -=7; // assigns to g the result of g - 7
g *=8; // assigns to g the result of g * 8
g /=4; // assigns to g the result of g / 4
g %=3; // assigns to g the result of g % 3
Unary operators act on only one variable, and binary operators act on two and return one value. Only the variable that receives the value as a result of the expression is changed.
Let's look at the simple expression c = b + 5. In this example, b + 5 is the binary operation. Only variable c is changed as it is assigned the value of whatever is in b plus 5. Variable b is unchanged. This seems to involve only common sense in a simple expression; however, more complex expressions do not appear so straightforward. It is possible to combine unary operations such as incrementing with binary operations to create a complex expression, changing more than one variable in the expression. Break these out in separate expressions if at all possible. This will help greatly in later debugging and maintenance.
Complex integer expressions require the use of separators to avoid unpredictable results. Separators such as parentheses notify the compiler of the order in which operations should be completed. In other words, separators set precedence. For example, the result of 2 * 3 + 4 is 10, but 2 * (3 + 4) is 14. In this situation, the separator overcomes the natural precedence of Java. This is a good time to discuss evaluation order.
Java evaluates from left to right, as in the following:
class test {
public static void main (String args [])
{
int ChangeYou, ChangeMe = 2;
ChangeYou = (ChangeMe+=2) * (ChangeMe+3); //this is the important line
System.out.println("ChangeMe = " + ChangeMe);
System.out.println("ChangeYou = " + ChangeYou);
}
}
This is the output of this code:
ChangeMe = 4
ChangeYou = 28
In this expression, the variables ChangeYou and ChangeMe are declared to be type int and initialized to 2. Then the value of variable ChangeMe is incremented by 2, resulting in 4. The new value of ChangeMe is added to 3, resulting in 7. The results of the two values in parentheses, 4 and 7, are now multiplied. This value, 28, is assigned to ChangeYou.
This is a very poor example of code readability. It would be much better to break out the expression into multiple expressions. This shows that just because something is possible does not mean it is good. However, it is a demonstration of how Java fully executes expressions from left to right.
Another place to remember that Java evaluates from left to right is among operators of the same precedence value. In Java, + and - are evaluated at the same level of precedence. Therefore, if both a + and a - operation occur in the same expression, Java will resolve the expression from left to right.
Table 6.12 lists operators from highest to lowest precedence.
Any operators that are on the same line are evaluated in the same
order.
Highest | Lowest | |||
[] | ||||
-- | instanceof | |||
new (type) expression | ||||
* | ||||
+ | ||||
<< | ||||
< | >= | |||
== | ||||
& | ||||
^ | ||||
&& | ||||
|| | ||||
?: | ||||
= |
The separators [] and () change precedence. Everything within these separators will be computed before Java looks outside them. Java will evaluate the expression in separators using its regular rules of precedence, as in the following expression:
ConfuseMe = (3 * 2 + 3) + (6 + 4 / 2);
The value of ConfuseMe is 17. Multiplication is of a higher precedence than addition, so 3 * 2 is the first operation, which is then added to 3. In the next set of parentheses, division is of a higher precedence than addition, so 4 / 2 is the first operation, the result of which is then added to 6. The values from each set of parentheses, 9 and 8, are then added together and assigned to variable ConfuseMe. Let's just hope that variable ConfuseMe has been typed as an integer!
You can use arrays and elements of arrays in the same way as any other variable. It is not necessary to use any intermediate variable. Here are some examples:
TestArray[0] = 4
TestArray[1] = TestArray[0] * 1996; //assigns the value of the first element
// TestArray multiplied by 1996 to
// the second element of array TestArray
TestArray[2]++ // increments the value of the third element
// of TestArray by one
AnotherArray[45] = "String data"; // assigns a string of data to the 46th
// element of AnotherArray
As you can see, arrays act as any other variable in an expression. The array structure is in place to group like items together for easy access.
Expressions are not just for numeric data types. Character variables are also assigned by means of an expression. Following is a code fragment that will get a character from the keyboard:
Note |
Many of the concepts in this code fragment have not been covered up to this point, but don't worry. We will get to them soon. |
/*
This is an example of getting a character from the keyboard and assigning
it to a variable
*/
//declares a new class of code
class GetCharFromKeyboard {
//declares a method within the class
public static void main ( String args[] )
//exception that could be generated here
throws java.io.IOException
//start block of code for this method
{
//declares KeyboardChar type char
char KeyboardChar;
//assign keyboard input to KeyboardChar
KeyboardChar = (char) System.in.read();
//prints KeyboardChar to screen
System.out.println(KeyboardChar);
//ends block of code for method main
}
//ends declaration of class GetCharFromKeyboard
}
The declaration statement defines the type of variable. This section gives more code examples of declaration use.
Declarations can happen anywhere in sections of code, although it is best for readability to group them together in the beginning of a code section.
Blocks are sections of code beginning and ending with curly braces ({}). Think of a block as a logical unit of code that can call methods, perform expressions, display output, and so on. A variable declared in a block is valid for that block and all its sub-blocks; this is the scope of the identifier. However, the scope of a variable does not move outward to the enclosing block; that is, a variable that is declared in a block has no meaning in the area outside its curly braces.
With Java you can reuse identifier names between a block and sub-block of code. This means that in memory there may be more than one identifier of a given name. Be careful with this feature! The compiler does not check which variable is being called as long as the type is consistent. |
Here is an example of hiding a variable by using a second declared variable of the same name:
class ShowHiding {
public static void main (String args[])
{
int TableTop;
TableTop = 2;
...
switch (AnyCommand) {
case '1':
int TableTop = 4; // TableTop has just been declared again
// and assigned a value of 4
break;
...
}
.... /*return to the main loop
System.out.println(TableTop); /*TableTop still has a value of 2*/
}
In this situation, the second time variable TableTop has been declared as valid for the case command block only. Any values assigned to it are good only for the scope of that block. When program execution returns to the main block, any values assigned to the new variable are lost. The upshot is that there must be a compelling reason to use a variable name more than once in a program. And be careful not to declare two variables with the same name accidentally!
The previous example also shows the combination of a declaration and an assignment statement:
int TableTop = 4;
This declares a variable of type int, with identifier name TableTop and a value of 4.
Variables of type integer are declared by the amount of memory space they will be allotted. (This is discussed more fully in the "Integer Data Types" section.) Following are examples of integer declarations:
byte ByteVar; //8 bits
short ShortVar; //16 bits
int IntVar; //32 bits
long LongVar; //64 bits
Floating-point variables are 32 or 64 bits in length. Following are examples of floating-point declarations:
float FloatVar; //32 bits
double DoubleVar; //64 bits
A character type variable holds only one character. This is different from the Strings class, which contains groups of characters. Remember, the char type holds an integer that references the Unicode character. The following sample code declares two character types:
char MyChar; // holds one character
char MyChar = 'y'; // declares variable MyChar and assigns y to it
Arrays are covered in depth in the "Data Types" and "Expressions" sections of this chapter. Arrays are one-dimensional lists of objects that are referenced by component numbers or subscripts. They can consist of other arrays, resulting in classic multidimensional arrays.
Arrays are declared as follows:
char MyCharArray[]; //one-dimensional array
char AnotherArray[][]; //two-dimensional array
int IntegerArray[]; //one-dimensional array of integers
int []IntegerArray; //equivalent to IntegerArray[]
This chapter has been explaining the basic elements of programming in Java: tokens, types, expressions, and declarations. Expressions, operators, and separators can be combined to manipulate data in a variety of ways. However, something is lacking: the ability to make decisions in the program. These decisions instruct the program which expression to solve or what data to assign to a variable. The solution to this problem is control flow. Control flow instructs the program how to make a decision and how further processing should proceed on the basis of that decision. Control flow gets your computer to start doing some of the thinking for you!
The building blocks of control flow are the { and } block delimiter characters and the if, while, do, for, and switch keywords. Each of these can be used to control how your program executes by determining if a condition is true and then executing a different section of code, based on the result. This is called a conditional expression.
A statement is any line of code ending in a semicolon. A statement can be an expression, a method call, or a declaration. A block is a group of statements that form a single compound statement. Think of blocks as statements logically grouped together for program flow and readability.
How do you tell Java where a block starts and ends? The characters { and } group such sections together. For example, the following is considered a block of code:
{
Store = "Grocery";
Item[0] = "spinach";
Item[1] = "tofu";
Item[3] = "rice";
}
When the program runs and gets to this block of code, it will begin execution at the beginning { and will not continue execution elsewhere until leaving the final }. The opening {, the closing }, and all code in between is considered a block. The curly braces must be paired one with one another or Java will not know where a block begins and ends.
Conditional expressions will generally execute one of several sections of code on the basis of a conditional test. This code can be as simple as a single statement, but more complex sections of code will be made up of many statements. Conditional expressions are used to make decisions in a program. They are used to evaluate whether a condition is true or false and will branch to different sections of code on the basis of the answer.
The simplest, but most important, conditional expression is the if statement. An if statement makes up a conditional expression of the form
if (expression) statement;
or
if (expression)
{
statement(s);
}
If the expression in the parentheses evaluates to the boolean true, statement is executed. If the expression evaluates to false, statement is skipped and execution continues at the statement following the if statement. The following code fragment shows how this works:
int Number; // declare variable
Number = System.io.read(); // get character from keyboard
if ( (Number % 2) == 0 )
System.out.println("even"); // test if number is even and
// print "even" if it is
Note that System.io.read will retrieve only one character at a time, so this program actually will handle only single-digit numbers. Only the first character will be tested if you enter a multiple-digit number.
In this example, the program reads a number from the keyboard. It is then tested to determine if it is even. If it is, the System.out.println statement is executed and the program terminates or continues to the next statement block.
Here is how the previous example can be extended:
int Number; // declare variable
Number =System.io.read(); // get number from keyboard
if ( (Number % 2) == 0 ) // test if number is even
{ // begin block
System.out.println("even"); // print message to screen
} // end block
There is an optional companion to the if statement that can extend its usefulness: the else statement. The statement following the if expression is executed only if the expression evaluates to true. The statement following the else is executed only if the if expression evaluates to false:
int Number; // declare variable
Number = System.io.read(); // get character from keyboard
if ( (Number % 2) == 0 ) // test if number is even
{ // begin if block
System.out.println("even"); // print message to screen
} // end if block
else // else if number not even
{ // begin else block
System.out.println("odd"); // print message to screen
} // end else block
You can also nest if statements within each other if you need a more complex multiway branch:
char KeyboardChar; / declare variables
int Number;
System.out.print("Enter number> "); // print prompt to screen
Number = System.in.read(); / get character from keyboard
if ( (Number % 2) == 0 ) / test if number is even
{ // begin if block
if ( Number < 5 )
{ // begin nested if block
System.out.println("even & < 5"); // print message to screen
} // end nested if block
else // else if number not < 0
{ // begin nested if block
System.out.println("even & >= 5"); // print message to screen
} // end nested if block
} // end if block
else // else if number not even
{ // begin else block
if ( Number < 5 )
{ // begin nested if block
System.out.println("odd & < 5"); // print message to screen
} // end nested if block
else // else if number not < 0
{ // begin nested if block
System.out.println("odd & >= 5"); // print message to screen
} // end nested if block
} // end else block
if statements are powerful and are the underpinnings for much of programming. The if statement is a fundamental control structure, because almost anything can be tested with one.
A variation on the if statement is the switch statement, which performs a multiway branch instead of a simple binary branch. switch statements are of the form
switch (expression)
{
case value:
statement(s);
break;
case value:
statement(s);
break;
.
.
.
default:
statement(s);
break;
}
The keyword switch begins the switch construct. The parentheses contain values that must evaluate to a byte, a char, a short, or an int. Next is a required {, followed by any number of constructs that begin with the keyword case and end with break, with any number of statements between. Next there is an optional section that begins with the keyword default and ends with break. Finally, a required } completes the case statement. This sounds complicated in description, but is obvious in usage, as demonstrated in the next example.
The case keywords are each followed by a value. This must also be of type byte, char, short, or int. The case statement itself works by evaluating the expression and then scanning down the case statements until an exact match is found. At this point the corresponding group of statements between the case and break will be executed. When the break is encountered, execution will resume at the first statement following the switch construct. If no matches are found and a default is present, the group of statements associated with default will be executed. If no default is present, execution will fall through the entire switch construct and do nothing, with execution again continuing after the end of the construct.
A case statement does not have to have a break associated with it. If break is not present, program execution falls through to the next case statement. It keeps executing that group of statements until a break is encountered, so be sure to place appropriate breaks if this is not the intended action.
Here is an example:
static void ParseChar (char KeyboardChar)
{
switch (KeyboardChar) {
case 'l':
System.out.println("left");
break;
case 'r':
System.out.println("right");
break;
case 'q': //note no break here, falls through
case '\n':
break;
case 'h': //note no break here either
default
System.out.println("Syntax: (l)eft, (r)ight, (q)uit");
System.out.println("Please enter a valid character");
KeyboardChar = '';
break;
}
}
In this example expression is a char type, and each case contains a corresponding char value.
Warning: |
A common programming error is to forget to put in a break where it is needed; the result is that unintended codes are executed. |
case statements cannot evaluate strings or objects, as is true with C and C++. Only items whose values can be evaluated as integers can be used in case statements. Remember, of course, that char is evaluated as an integer. Many times you will wish case statements could handle more complex objects. If you are working with one of the allowed expression types or can build some sort of index that uses one of these types, the switch statement can be an efficient and easily understood method for doing complex branching.
Looping expressions generally continue to loop through a section of code until a certain condition is met. Some looping expressions check the condition before executing the code. Other looping expressions check the condition after executing the code.
The while loop is a construct that repeatedly executes a block of code as long as a boolean condition remains true. The initial while expression is evaluated first. It is possible for the statements making up a while loop never to execute if the initial expression evaluates to false. while loops are of the form
while ( expression ) statement;
or
while ( expression )
{
statement(s);
}
The keyword while begins the while construct. The parentheses contain an expression, which must evaluate to a boolean. This is followed by a statement or a block.
When a while loop is encountered, the expression is evaluated first. If it evaluates to true, the statement or block following the while statement, known as the body of the while loop, is executed. When the end of the body is reached, the expression is evaluated again. If it is false, execution will continue with the next statement following the while loop. If it is true, the body of the while loop will be executed again. The body will continue to be executed until the expression evaluates to false.
Here is an example:
char KeyboardChar =(char)System.in.read();
while (KeyboardChar != 'q')
{
ProcessChar(KeyboardChar);
KeyBoardChar = (char)System.in.read();
}
This expression tests whether KeyboardChar is equal to the character q. If it is not, the body of the while loop will be executed, which will process the character and then read in another. This will continue until the character q is read in. Notice in this example that KeyboardChar has been initialized by reading a character before the loop is executed. If it was not explicitly set, the while loop might never be entered, depending on what value KeyboardChar had. This is another common programming error. Even if KeyboardChar were initialized to something, it would be processed before the user had a chance to type in a character.
A while loop continues to loop until the expression evaluates to false. It is possible for a while loop to execute forever if the expression never evaluates to false; this is known as an infinite loop. A common cause of infinite loops is that the programmer forgot to put a statement that changes part of the expression in the body of the loop. If the expression never changes, it will always evaluate the same and an infinite loop occurs.
The do loop enables your code to repeatedly execute a block of code until a boolean expression evaluates to false. It is almost identical to the while loop, except the expression is evaluated at the bottom of the loop rather than the top. This means that the contents of the loop will always be executed at least once. do loops are of the form
do statement; while ( expression );
or
do
{
statement(s);
} while ( expression );
The keyword do begins the do construct. This is followed by a statement or a block. Next is the keyword while, followed by the parentheses containing an expression that must evaluate to a boolean.
When a do loop is encountered, the statement or block following the do keyword is executed. When the do loop body completes, expression is evaluated. If it is false, execution will continue with the next statement following the do loop. If it is true, the body of the do loop will be executed again. The body will continue to be executed until the expression evaluates to false.
Here is an example:
do
{
KeyBoardChar = (char)System.in.read();
ProcessChar(KeyboardChar);
} while (KeyboardChar != 'q')
In this example, the body of the while loop is executed, which reads in a character and then processes it. The expression is then evaluated to determine if KeyboardChar is equal to the character q. If it is, execution will continue with the first statement after the do loop. If it is not, the do loop body will be executed again. This will continue until the character q is read in.
Compare this version with the while loop version shown previously. It does not need an initialization of KeyboardChar because the variable will be read in at the beginning of the loop. This is the most common reason for choosing a do over a while loop.
Like with the while loop, it is possible to create an infinite loop by forgetting to put in the body of the loop a statement that changes part of the expression.
The for loop enables code to execute repeatedly until a boolean expression evaluates to false. It is similar to a while loop but is more specialized. As in a while loop, the expression is evaluated at the top of the loop. However, it provides a more explicit means for initializing a loop variable and modifying it at the end of the loop. for loops are of the form
for (initialization; expression; modification) statement;
or
for (initialization; expression; modification)
{
statement(s);
}
The keyword for begins the for construct. The parentheses contain an initialization, an expression, and a modification. The initialization can be a statement of any kind, but typically its purpose is to initialize part of the expression. Initialization is followed by a semicolon (;), followed by an expression. Like the while and do loops, the expression must evaluate to a boolean. This is followed by another semicolon and then modification. modification also can be any statement, but again is typically used to modify part of the expression. Finally, this is followed by a statement or a block.
When a for loop is encountered, initialization is first executed, and then the expression. If it evaluates to true, the statement or block following the while statement is executed. This statement or block is known as the body of the for loop. When the end of the body is reached, modification is executed. The expression is then evaluated again. If it is false, execution continues with the next statement following the for loop. If it is true, the body of the for loop is executed again. The body continues to be executed until the expression evaluates to false.
Here is an example:
for (char KeyboardChar=ProcessChar(KeyboardChar); KeyboardChar != 'q';
KeyBoardChar=(char)System.in.read())
{
ProcessChar(KeyboardChar);
}
In this example, KeyboardChar is initialized by reading in a character. expression is then evaluated to determine if KeyboardChar is equal to the character q. If so, execution will continue with the first statement after the for loop. If not, the body of the for loop will be executed, which will process KeyboardChar. Next, another character will be read in. This will continue until the character q is read in.
You can use for loops to move through a range of numbers. This is used in conjunction with arrays or other indexes. Here's an example:
int Array;
for (I=0; I < ArraySize; I++)
{
if (Array[i] < 0)
{
System.out.println("ERROR: negative number encountered, index = " + I);
}
else
{
ProcessArray(Array[i]);
}
}
This for loop will initialize I to a value of zero and then step through Array, checking for negative numbers before processing an entry. This is a compact, easily assimilated method of writing code.
Like with the while and do loops, it is possible to create an infinite loop by forgetting to put a statement that changes part of the expression in the body of the loop.
The break construct can be used to break out of the middle of a for, do, or while loop. (This chapter has already discussed how to use it to break out of a switch statement.) When a break statement is encountered, execution of the current loop immediately stops and resumes at the first statement following the current loop.
Here is how the previous for loop example could be extended:
int ix;
for (ix=0; ix < ArraySize; ix++)
{
if (Array[ix] < 0)
{
System.out.println("ERROR: negative number encountered, index = " + ix);
break;
}
ProcessArray(Array[ix]);
}
Again, this code will loop through Array looking for negative entries. However, by including the break statement, execution of this for loop will stop at the first negative entry. In this example, a negative entry might be considered so severe that no other processing should be done. Also notice that no else statement is needed because if an error occurs, execution will jump to the end of the loop, skipping the entry-processing code.
The continue construct can be used to short-circuit parts of a for, do, or while loop. When a continue statement is encountered, execution of the current loop immediately resumes at the top, skipping all other code between it and the end of the loop.
The following for loop uses the continue construct:
int ix;
for (ix=0; ix < ArraySize; ix++)
{
if (Array[ix] < 0)
{
System.out.println("ERROR: negative number encountered, index = " + ix);
continue;
}
ProcessArray(Array[ix]);
}
Again, this code will loop through Array looking for negative entries. However, the inclusion of the continue statement means that execution of this for loop will not continue in the body of the loop if a negative entry is encountered. In this example, a negative entry might be considered illegal and should not be processed. However, it is not so severe that it stops processing other entries. Again, notice that no else statement is needed because if an error occurs, execution will continue at the top of the loop, skipping the entry-processing code.
If break and continue only take you to the end or beginning of the current loop, what do you do if you have nested loops and need to get out of more than just the current one? Java provides an extended version of break and continue for just this purpose. By adding a label to a loop and referencing it in a break or continue statement, you can make execution continue at the end or beginning of the loop of your choice.
Here's an example:
err:
for (ix=0; ix < ArraySize; ix++)
{
for (j=0; j < ArraySize; j++)
{
if (Array[ix][j] < 0)
{
System.out.println("ERROR: negative number encountered, index = " + ix + "," + j);
break err;
}
ProcessArray(Array[ix][j]);
}
}
In this example, Array is extended to two dimensions, and two for loops are used to step through all elements of the array. If a negative entry is encountered, execution will branch to the end of the for loop labeled err, rather than the normal inner one. Without this construct, you would need to set an additional flag variable and test it in the outer loop. The label itself must immediately precede the intended loop; if it is placed anywhere else in the code, a compile-time error will occur.
The capability to jump out of the middle of a loop is handled in C++ and some C implementations with a goto statement. The goto can branch anywhere in the code, which can lead to what is known as spaghetti code. Of course, spaghetti code is something we recognize in other people's code, but never our own! The labeled loop concept in Java provides breakout capability while limiting the scope. It is a good compromise.
This chapter covers the most basic parts of the Java programming language. It gives many examples of ways to use tokens, literals, data types, expressions, declarations, and control flow. Together these form the fundamentals of any program you write or application you develop in Java. This chapter is also a good reference for correct syntax. You will be well on your way to developing powerful applications in Java when you combine these fundamentals with the information covered in the next chapter.