This chapter will answer the following questions:
Before you can effectively read or write programs in any language, you need to know about the language's syntax rules and restrictions. A language's syntax defines the way programs are written in that language; more specifically, the syntax of the language defines the language elements, the way these elements are used, and the way they are used together. The following lists the typical language elements and shows how the language syntax is concerned with these elements:
An identifier is a name that uniquely identifies a variable, a method, or a class (we will discuss variables later in this chapter; methods and classes are discussed in a later chapter). In most languages, there are restrictions on how identifiers are composed. The following lists Java's restrictions on identifiers:
Since some C library names begin with an underscore or a dollar sign, it is best to avoid beginning an identifier name with these characters. Importing a C library into a program that uses an underscore or a dollar sign to start an identifier name might cause name clashing and confusion.
In addition to these restrictions, certain conventions are used with identifiers to make them more readable. Although these conventions do not affect the compiler in any way, it is considered a good programming practice to follow them. The following table lists some of these conventions based on the type of identifier:
Type of Identifier |
Convention |
Examples |
Class name |
The first letter of each word is capitalized |
Mammal, SeaMammal |
Function name |
The first letter of each, except the first, word is capitalized |
getAge, setHeight |
Variable name |
The first letter of each, except the first, word is capitalized |
age, brainSize |
Constant names |
Every letter is capitalized and underscores are used between words |
MAX_HEIGHT, MAX_AGE |
A literal, or constant, represents a value that never changes. Think of an identifier as something that represents a value, whereas a literal is a value. For example, the number 35 is a literal; the identifier age represents a number which could be 35. In Java, a literal can be a number (integer or floating-point), a Boolean, a character, or a string.
Integer literals are written in three formats: decimal (base 10), hexadecimal (base 16), and octal (base 8). Decimal literals are written as ordinary numbers, hexadecimal literals always begin with 0X or 0x, and octal literals begin with 0. For example, the decimal number 10 is 0xA or 0XA in hexadecimal format, and 012 in octal format.
An integer literal can be stored in the data types byte, short, int, or long. By default, Java stores integer literals in the int data type, which is restricted to 32-bits.
To store an integer literal in the long data type, which can store 64-bit values, add the character l or L to the end of the literal. For example, the literal 9999L is stored as long. The following lines of code use integer literals:
int x = 12345; //12345 is a literal
in
t y = x * 4; //4 is a literal
In the first line, the literal 12345 is stored directly in the int variable x. In the second line, the literal 4 is used to compute a value first, which in turn is stored in the int variable y.
Note that even though an integer literal represents a constant value, it can still be assigned to an integer variable. Think of the variable as a storage unit that at any one time can represent a single literal value. This also applies to the other literal types.
A floating-point literal is a number with a decimal point and/or exponent. A floating-point literal is written in either standard or scientific notation. For example, 123.456 is in standard notation, while 1.23456e+2 is in scientific notation.
Floating-point literals are stored in the 64-bit double type (the default type), or the 32-bit float type. To store a floating-point literal in the float type, append the letter f or F to the end of the number.
A Boolean literal represents two possible states: true or false. Boolean literals are stored in the data type boolean. Unlike C/C++ where the states of a Boolean value are represented by 0 (false) and 1 (true), Java represents these states using the keywords true and false.
A character literal represents a single Unicode character. Character literals are always surrounded by single quotes; for example, 'A' and '9' are character literals. Java uses the char type to store single characters.
The Unicode character set is a 16-bit set that supplants the 8-bit ASCII set. The Unicode set can define up to 65,536 values, which is enough to include symbols and characters from other languages. Check out the Unicode home page at www.unicode.org for more information.
A special type of character literal is called an escape sequence. Like C/C++, Java uses escape sequences to represent special control characters and characters that cannot be printed. An escape sequence is represented by a backslash (\) followed by a character code. The following table summarizes these escape sequences:
Character |
Escape Sequence |
Backslash |
\\ |
Backspace |
\b |
Carriage return |
\r |
Continuation |
\ |
Double quote |
\" |
Form feed |
\f |
Horizontal tab |
\t |
Newline |
\n |
Octal character |
\DDD |
Single Quote |
\' |
Unicode character |
\uHHHH |
An octal character is represented by a sequence of three octal digits, and a Unicode character is represented by a sequence of four hexadecimal digits. For example, the decimal number 57 is represented by the octal code \071, and the Unicode sequence \u0039.
To illustrate the use of escape sequences, the string in the following statement prints out the words Name and ID separated by two tabs on one line, and prints out "Joe Smith" and "999", also separated by two tabs, on the second line:
String escapeDemo = new String ("Name\t\tID\n\"Joe\ Smith\"\t\t\"999\"");
Note that this statement is intentionally written on two lines; therefore, the continuation character (\) is used to prevent a compiler error.
A string literal represents a sequence of characters. Strings in Java are always enclosed in double quotes. Java handles strings differently than
C/C++; the latter represents a string using an array of characters, while the former uses the classes String and StringBuffer. So, of all the literal types we've discussed, only string literals are stored as objects by default. Strings are covered in more detail in the section "Arrays and Strings."
A keyword is a predefined identifier that has a special meaning to the Java compiler, and which cannot be redefined. The following is a list of Java's keywords:
abstract |
else |
int |
static |
boolean |
extends |
interface |
super |
break |
false |
long |
switch |
byte |
final |
native |
synchronized |
byvalue* |
finally |
new |
this |
case |
float |
null |
throw |
cast* |
for |
operator* |
throw s |
catch |
future* |
outer* |
transient |
char |
generic* |
package |
true |
class |
goto* |
private |
try |
const* |
if |
protected |
var* |
continue |
implements |
public |
void |
default |
import |
rest* |
volatile |
do |
inner* |
return |
while |
double |
instanceof |
short |
|
* Reserved but not being used.
As you may have noticed, many of Java's keywords are borrowed from C/C++. Also, as in C/C++, keywords are always written in lowercase. Generally speaking, Java's keywords can be categorized according to their function as follows (examples are in parenthesis):
A statement represents a single command, or line of code, for the compiler. This doesn't mean, however, that each line of code is a statement; in other words, there is no one-to-one mapping between physical lines of codes and statements. As we will see later in this chapter, some statements, such as an if statement, can be composed of multiple lines of code.
So, if a statement can take up multiple physical lines, how does the compiler know where each statement ends and the next begins? By using semicolons to separate statements.
The Java compiler is not concerned with the length of each statement, as long as statements are always separated by semicolons. For example, the following two statements are equivalent:
x = (y + z) / q; //statement 1 x = (y + z ) / q; //statement 2
The second statement has whitespace characters embedded in it (whitespace characters are the space, horizontal and vertical tabs, form-feed, and new-line). Although the Java compiler ignores all whitespace characters embedded in statements, it is obviously bad practice to do that since it makes the code difficult to read.
Recall that in the case of string values, the continuation character ( \ ) must be used at the end of each line to allow strings to take up multiple lines.
A code block is a grouping of statements that behave as a unit. Java delimits code blocks with braces ({ and }). Examples of code blocks are class definitions, loop statements, condition statements, exception statements, and function bodies. In the following section of code, there are three code blocks: the function frmResolver(), the try block, and the catch block.
public frmResolver() { try { jbInit(); } catch (Exception e) { e.printStackTrace(); } }
The above code also illustrates the concept of nested blocks: the try and catch blocks are nested inside the main frmResolver() block.
Comments are natural-language statements written by the programmer to make notes about the code. There are three styles of comments in Java. The first one begins with /* and ends with */, and allows you to write comments that span multiple lines. This style is the same as in the C language.
The following code demonstrates the use of this style:
x = y + z; /* This is a comment.*/ z = q / p; /*This comment extends over two lines*/
When the Java compiler encounters /*, it ignores every character that follows it until it encounters */.
The second comment style is similar to the first one, only it begins with /** and ends with */. The difference is that this style is used with the JDK tool javadoc to automatically generate documentation from the source code (Java documentation is beyond the scope of this course).
The third comment style is borrowed from C++. It begins with // and can be written on just one line. Here's an example:
x = y + z; //This comment cannot extend over multiple lines
Nesting comments is valid only when comments of the third style are embedded in one of the other two styles. Nesting comments of the first two styles is illegal.
Here is an invalid nested comment:
/*This is the beginning of the comment /* The comment ends here */ this is outside the comment and will generate a compiler error */
As we mentioned earlier, the compiler ignores everything between /* and */; so when it encounters the first */ it thinks that the comment ended. The last line in the code is therefore not contained in the comment.
The following is an example of a valid nested comment:
/*This is the beginning of the comment //This is OK //so is this this is the end of the comment. */
An expression is a meaningful combination of identifiers, keywords, symbols, and operators that has a value of some sort. Generally speaking, everything that can be used on the right side of an assignment sign is an expression.
Here are some examples of expressions:
s = "Hello World"; x = 123.4; y = (x * 5) / 2; value = getValue(); Mammal m = new Mammal();
From the previous examples, we can categorize expressions into the following:
Operators are special symbols that perform a particular function on operands. There are five general types of operators: arithmetic operators, logical operators, comparison operators, assignment operators, and bitwise operators. Each of these can be further categorized into unary and binary. Unary operators operate on a single operand, while binary operators operate on two operands.
In the following sections, we will examine the different types of operators. In addition, we will discuss the operator associativity and precedence. Precedence determines the priority of operators, while associativity determines the operating order of operators of equal precedence used in a single statement.
Java provides a full set of operators for mathematical calculations. Java, unlike some languages, can perform mathematical functions on both integer and floating-point values. You will probably find these operators familiar.
The following table lists the arithmetic operators:
Operator |
Definition |
Precedence |
Associativity |
++/-- |
Auto-increment/decrement |
1 |
Right |
+/- |
Unary plus/minus |
2 |
Right |
* |
Multiplication |
4 |
Left |
/ |
Division |
4 |
Left |
% |
Modulus |
4 |
Left |
+/- |
Addition/subtraction |
5 |
Left |
The modulus operator returns the remainder of dividing its first operand by its second. The auto-increment/decrement operators are unary operators. They modify the value of their operand by adding or subtracting 1 to their value. When used in expressions, the outcome of the auto-increment/decrement operation depends on whether the operator precedes or succeeds the operand.
The following demonstrates this:
int y = 3, x, z; x = ++y; z = y--;
In the second statement, the y variable is incremented by 1, and then its new value (4) is assigned to x. In the third statement, the auto-decrement operation takes place following the assignment of y's current value to z. In other words, the current value of y (4) is assigned to z, then y is modified to be 3.
The following code illustrates how precedence and associativity affect operators:
int x = 1, y = 2, z = 3, i, j; i = x + y * z; //same as i = x + (y * z) j = ++x + -y; //same as j = (++x) + (-y) i = x++ + -y; //same as i = x++ + (-y)
Logical (or Boolean) operators allow the programmer to group Boolean expressions to determine certain conditions. These operators perform the standard Boolean operations (AND, OR, NOT, and XOR).
The following table lists the logical operators:
OperatorDefinitionPrecedence |
Associativity |
|
|
! |
Unary logical complement (NOT) |
2 |
Right |
& |
Evaluation AND |
9 |
Left |
^ |
XOR |
10 |
Left |
| |
Evaluation OR |
11 |
Left |
&& |
Short-circuit AND |
12 |
Left |
|| |
Short-circuit OR |
13 |
Left |
The evaluation operators always evaluate both operands. The short-circuit operators, on the other hand, always evaluate the first operand, and if that determines the value of the whole expression, they don't evaluate the second operand. For better understanding, consider the following code:
if ( !isHighPressure && (temperature1 > temperature2)) { } boolean1 = (x < y) || ( a > b); boolean2 = (10 > 5) & (5 > 1);
The first statement evaluates !isHighPressure first, if it is false, it does not evaluate the second operand (temperature1 > temperature2) since the first operand being false means the whole expression is false. The same is true for the second statement-the value of boolean1 will be true only if x is less than y (the value of the second operand is never determined). In the third statement, however, the compiler will compute the values of both operands before making the assignment to boolean2.
The XOR operator produces a true value only if the operands are of different values (true and false, or false and true).
Programmers need the ability to compare values. Comparison operators, unlike logical operators, will only evaluate a singe expression.
The following table lists the comparison operators:
Operator |
Definition |
Precedence |
Associativity |
< |
Less than |
7 |
Left |
> |
Greater than |
7 |
Left |
<= |
Less than or equal |
7 |
Left |
>= |
Greater than or equal |
7 |
Left |
= = |
Equal |
8 |
Left |
!= |
Not equal |
8 |
Left |
The equality operator can be used to compare two object variables of the same type (objects are discussed in the next chapter). In this case, the result of the comparison is true only if both variables refer to the same object. Here is a demonstration:
m1 = new Mammal(); m2 = new Mammal(); boolean b1 = m1 == m2; //b1 is false m1 = m2; boolean b2 = m1 == m2; //b2 is true
The result of the first equality test is false because m1 and m2 refer to different objects (even though they are of the same type). The second comparison is true because both variables now represent the same object.
Most of the time, however, the equals() method is used to compare objects. This method, defined in the Object class, must be implemented in a class subclassed from Object, before objects of the class can be compared for equality.
Java, like all languages, allows you to assign values to variables. The following table lists assignment operators:
Operator |
Definition |
Precedence |
Associativity |
= |
Assignment |
15 |
Right |
+= |
Add and assign |
15 |
Right |
-= |
Subtract and assign |
15 |
Right |
*= |
Multiply and assign |
15 |
Right |
/= |
Divide and assign |
15 |
Right |
&= |
AND with assignment |
15 |
Right |
|= |
OR with assignment |
15 |
Right |
^= |
XOR with assignment |
15 |
Right |
The first operator should be familiar by now. The rest of the assignment operators perform an operation first, and then store the result of the operation in the operand on the left side of the expression. Here are some examples:
int y = 2; y *= 2; //same as (y = y * 2) boolean b1 = true, b2 = false; b1 &= b2; //same as (b1 = b1 & b2)
Bitwise operators are of two types: shift operators and Boolean operators. The shift operators are used to shift the binary digits of an integer number to the right or the left. Consider the following example (the short integer type is used instead of int for conciseness):
short i = 13; //i is 0000000000001101 i = i << 2; //i is 0000000000110100 i >>= 3; //i is 0000000000000110
In the second line, the bitwise left shift operator shifted all the bits of i two positions to the left. The bitwise right shift operator then shifted the bits three positions to the right.
The shifting operation is different in Java than in C/C++ -mainly in how it is used with signed integers. A signed integer is one whose left-most bit is used to indicate the integer's sign (the bit is 1 if the integer is negative). In Java, integers are always signed, whereas in C/C++ they are signed by default. In most implementations of C/CC, a bitwise shift operation does not preserve the integer's sign (since the sign bit would be shifted out). In Java, however, the shift operators preserve the sign bit (unless you use the >>> to perform an unsigned shift). This means that the sign bit is duplicated, then shifted (right shifting 10010011 by 1 is 11001001).
The following is a complete list of Java's bitwise operators:
Operator |
Definition |
Precedence |
Associativity |
~ |
Bitwise complement |
2 |
Right |
<< |
Signed left shift |
6 |
Left |
>> |
Signed right shift |
6 |
Left |
>>> |
Zero-fill right shift (as if unsigned) |
6 |
Left |
& |
Bitwise AND |
9 |
Left |
| |
Bitwise OR |
10 |
Left |
^ |
Bitwise XOR |
11 |
Left |
<<= |
Left-shift with assignment |
15 |
Left |
>>= |
Right-shift with assignment |
15 |
Left |
>>>= |
Zero-fill right shift with assignment |
15 |
Left |
We said earlier that there are two types of operators: unary and binary. That's not exactly true. There is also a ternary operator that Java borrows from C, the ?: operator. Here's the general syntax for this operator:
expression1? expression2: expression3;
expression1
is first evaluated. If its value is true, expression2 is computed, otherwise expression3 is. Here is a demonstration:int x = 3, y = 4, max; max = (x > y)? x: y; //this is basically the same as max=x;
In this code, max is assigned the value of x or y, based on whether x is greater than y.
Some people mislabel this operator as being a conditional statement. It is not a statement. The following invalid code illustrates why it is not a statement:
(x > y)? max = x: max = y; //can't use it as if it's a statement
Data types are entities, which represent specific types of values that can be stored in memory, and are interpreted in a specific way by the compiler. We already introduced data types in our discussion about literals in a previous section. We mentioned that a literal is stored in a certain data type depending on the literal's value; the literal 9, for example, can be stored in the int data type, and the literal 'c' can be stored in the char data type.
There are two categories of data types in Java: built-in and composite data types. Built-in (or primitive) data types can be further categorized into three kinds: numeric, Boolean, and character data types. Built-in data types are understood by the compiler and don't require special libraries. (A special library basically refers to any collection of code that is not part of the actual language definition). Composite types are of two kinds: arrays and strings. Composite types usually require the use of special libraries.
Before explaining the different Java data types, we need to discuss variables.
We defined a data type as something representing a specific value that can be stored in memory. So, how do you allocate memory for that value, and how do you access it and assign values to it? To allocate a portion of memory for the storage of data types, you must first declare a variable of that data type, then give the variable a name (identifier) that references it. Here's the general syntax of a variable declaration:
datatype identifier [ = defaultValue ];
The declaration begins with the type of variable, followed by the variable's identifier, then followed by an optional default value assignment. The following are examples of different types of variable declarations:
int p; //declares the variable p to store int data types float x, y = 4.1, z = 2.2; boolean endOfFile = false; char char1 = 'T';
Notice that the second line declared three variables at the same time. You can declare multiple variables of the same type at once, as long as you separate the variable identifiers with commas.
A variable can be declared anywhere in a program, as long as its declaration precedes any reference to it. Java is a strongly typed language, which means that all variables must be declared before they are used.
If we attempt to reference the variable x, without declaring it first, we would get a compiler error:
int y = 4, z = 2; x = y / z; //What is x? Is it a float, char, int, or what?
In the code example above, the second line generates an error because the compiler does not know the type of x; moreover, it does not know where in memory it is stored.
To avoid any problems, such as referencing a variable that does not yet exist, it is best to declare all variables at the beginning of the code blocks where they are used. That makes it easier for you to keep track of all your variables.
Now that we understand what variables are, we can go on to discuss data types.
The numeric data types are summarized in the following table:
Type |
Size |
Description (smallest and largest positive values) |
byte |
8 bits |
very small signed integer (-128 Þ 127) |
short |
16 bits |
short signed integer (-32768 Þ 32767) |
int |
32 bits |
signed integer (-2.14e+9 Þ 2.14e+9) |
long |
64 bits |
long signed integer (-9.22e+18 Þ 9.22e+18) |
float |
32 bits |
floating-point number (1.402e-45 Þ 3.402e+38) |
double |
64 bits |
double precision floating-point (4.94e-324 Þ 1.79e+308) |
If a numeric variable is not initialized by the programmer, the Java VM will automatically initialize it to 0. Most Java compilers will also detect uninitialized variables. This is different than C/C++, where uninitialized variables contain random values and are not detected by the compiler.
A Boolean data type has two values: true and false. Unlike C/C++, which stores Boolean data types numerically (0 for false and 1 for true), Java uses the built-in data type boolean. Uninitialized boolean variables are automatically set to false. The following code illustrates the use of a boolean variable:
int a = 1, b = 0; boolean bool = a < b; //bool is false
Java uses the data type char to store a single Unicode character. Java's char type, therefore, is 16-bit wide, whereas in C/C++, it is (by default) 8-bits wide.
An array is a data structure, which can hold multiple elements of the same type. The array's element type can be anything: a primitive type, a composite type, or a user-defined class. If you have used arrays in other languages, you will probably find the way Java handles arrays interesting. Let's first see some examples of array declarations:
int studentID[]; char[] grades; float coordinates[][];
There are two things to note about these array declarations:
The previous array declarations did not actually allocate any memory for the arrays (they simply declared identifiers that will eventually store actual arrays). For that reason, the sizes of the arrays were not specified.
To actually allocate memory for the array variables, you must use the new operator as follows:
int studentID[] = new int[20]; char[] grades = new char[20]; float[][] coordinates = new float[10][5];
The first statement creates an array of 20 int elements, the second creates an array of 20 char elements, and the third creates a two-dimensional 10 by 5 float array (10 rows, 5 columns). When the array is created, all its elements are null.
The use of the new operator in Java is similar to using the malloc command in C and the new operator in C++.
To initialize an array, the values of the array elements are enumerated inside a set of curly braces. For multi-dimensional arrays, nested curly braces are used.
The following statements illustrate this:
char[] grades = {'A', 'B', 'C', 'D', 'F'); float[][] coordinates = {{0.0, 0.1}, {0.2, 0.3}};
The first statement creates a char array called grades. It initializes the array's elements with the values 'A' through 'F'. Notice that we did not have to use the new operator to create this array; by initializing the array, enough memory is automatically allocated for the array to hold all the initialized values. Therefore, the first statement creates a char array of 5 elements.
The second statement creates a two-dimensional float array called coordinates, whose size is 2 by 2. The array's first row is initialized to 0.0 and 0.1, and the second row to 0.2 and 0.3. Conceptually, coordinates is an array of two array elements.
Array elements are accessed by subscripting (or indexing) the array variable. Indexing an array variable involves following the array variable's name with the element's number (index) surrounded by square brackets. Arrays are always indexed starting from 0. In the case of multi-dimensional arrays, you must use an index for each dimension to access an element.
Here are a couple of examples:
firstElement = grades[0]; //firstElement = 'A' fifthElement = grades[4]; //fifthElement = 'F' row2Col1 = coordinates[1][0]; //row2Col1 = 0.2
The following snippet of code demonstrates the use of arrays. It creates an array of 5 int elements called intArray, then uses a for loop to store the integers 0 through 4 in the elements of the array:
int[] intArray = new int [5]; int index; for (index = 0; index < 5; index++) intArray [index] = index;
We will discuss for loops in a later section. Basically this code uses the loop to increment the
index variable from 0 to 4, and at every pass, it stores its value in the element of intArray indexed by index.A string is a sequence of characters. Java uses the String data type to store strings. This data type is a member of the java.lang package, which we will study in the "Java Class Libraries" chapter. That means that it is not a built-in type; if you want to declare a variable of type String, you must use the java.lang package. We will learn more about packages in the "Object-Oriented Programming in Java" chapter.
A String variable, once initialized, cannot be changed. How can it be a variable and yet cannot be changed? Recall that a variable is just a reference to a place in memory; you use it to access and change the memory in which it points. In the case of a String variable, the memory that a String variable points to cannot be changed; however, the variable itself can be made to point somewhere else in memory. The following code illustrates this:
String s = new String ("Hello"); s = "Hello World'; //s now points to a new place in memory
We first created a String variable called s that pointed to a particular place in memory, which contains the string "Hello". The second line made s point to a new place in memory, which now contains "Hello World". This is valid because we changed the variable itself, not the memory it points to. This point illustrates the difference between a variable and the memory it points to.
If you want more control over your strings, use the StringBuffer class. This class, also part of the java.lang package, provides methods that allow you to modify the contents of strings. Here's an example of something you cannot do using the String class:
StringBuffer s = new StringBuffer ("Hello"); s.setCharAt (1, 'o'); //s is now "Hollo"
StringBuffer's setCharAt() method modifies the character, at the index specified in the first parameter, to the new value specified in the second parameter.
We will cover both string classes in more detail in the chapter, "The Java Class Libraries".
In some cases, you need to convert a variable's type to another type. You might, for example, need to pass an int variable to a method that accepts only float variables. Converting the type of a variable is called casting. To cast a variable's type, place the type you want it to cast to, in parentheses, immediately before the variable's identifier. The following example shows how a method's return variable, which is of type int, can be cast to float:
float f = (float) returnInt();
You must be careful when casting types, as some loss of information might result. Casting a 64-bit long variable to a 32-bit int variable, for instance, causes the compiler to discard the upper 32-bits of the long variable. If the value of the long variable at the time of the cast were bigger than 32-bits, the cast would assign an incorrect value to the int variable.
The general rule is that the cast type must be at least equal in size to the original type. The following table shows the casts that do not result in information loss:
Original Type |
Cast Type |
byte |
short , char, int, long, float, double |
short |
int , long, float, double |
char |
int , long, float, double |
int |
long , float, double |
Long |
float , double |
Float |
double |
There are times when a cast is performed implicitly by the compiler. The following is an example:
if (3 > 'a') { }
In this case, the value of 'a' is converted to an integer value (the ASCII value of the letter, a) before it is compared with the number 3.
Scope rules determine where in a program a variable is recognized. Variables fall into two main scope categorizes:
Scope rules are tightly related to code blocks. The one general scope rule is as follows: a variable declared in a code block is visible only in that block and any blocks nested inside it. The following code illustrates this:
class scopeDemo { int x = 0; void method1() { int y; y = x; //legal } void method2() { int z = 1; z = y; //illegal! } }
This code declares a class called scopeDemo, which has two methods: method1() and method2(). The class itself is considered the main code block, and the two methods are its nested blocks.
If you're not familiar with classes, just think of scopeDemo as being the actual program, and the two methods, its functions. We will cover classes and methods in the "Object-Oriented Programming in Java" chapter.
The x variable is declared in the main block, so it is visible (recognized by the compiler) in both method1() and method2(). Variables y and z, on the other hand, were declared in two independent, nested blocks; therefore, attempting to use y in method2() is illegal since y is not visible in that block.
A program that relies on global variables can be error-prone for two reasons: (1) global variables are difficult to keep track of, and (2) a change to a global variable in one part of the program can have an unexpected side effect in another part of the program. Local variables are safer to use since they have a limited life span. For example, a variable declared inside a method can be accessed only from that method, so there is no danger of it being misused somewhere else in the program.
Each statement in a program is executed once. However, it is sometimes necessary to execute one or more statements several times until a condition is met. Java provides three ways to loop statements: while, do and for loops.
The while loop is used to create a block of code that will execute as long as a particular condition is met. The following is the general syntax of the while loop:
while (condition) { code to execute in a loop }
The loop first checks the condition. If the condition's value is true, it executes the entire block. It then reevaluates the condition, and repeats this process until the condition becomes false. At that point, the loop terminates its execution. The following is a simple example:
int x = 0; //print "Looping" 10 times while (x < 10){ System.out.println("Looping"); x++; }
When the loop first starts executing, it checks whether the value of x(0) is less than 10. Since it is, the body of the loop is executed. In this case, the word "Looping" is printed on the screen, and then the value of x is incremented. This loop continues until the value of x is equal to, or greater than, 10. At that point, the loop terminates.
Make sure there is some point in the loop where the condition's value becomes false, and the loop terminates; otherwise, your loop would execute forever.
You can also terminate loop execution by using the return, continue, or break statements. The return statement is illustrated in the "Object-Oriented Programming in Java" chapter; the break and continue statements are discussed in the next section.
The do loop is similar to the while loop, except it evaluates the condition after the statements-not before. The following code shows the previous while loop converted to a do loop:
int x = 0; do{ System.out.println("Looping"); x++; }while (x < 10);
The main difference between the two loop constructs is that unlike the while loop, the do loop is always guaranteed to execute at least once.
The for loop is the most powerful loop construct. Here is the general syntax of a for loop:
for (init expr; condition expr; operation expr) { }
The for loop initialization consists of three parts: an initialization expression, a condition expression, and an "operation" expression. The third expression usually updates the loop variable initialized in the first expression. Here is the for loop equivalent of our initial while loop:
for (int x = 0; x < 10; x++){ System.out.println("Looping"); }
This loop and its equivalent while loop are practically the same. In fact, for (almost) every for loop, there is an equivalent while loop. The way the for loop executes is different, however. The first expression is executed first (in this case, x is initialized). The condition is then checked, and if it is true, the body of the loop is executed. Once the loop finishes executing, the third loop expression is computed (x is incremented). The cycle then returns to the first expression. It is critical that you understand how this works; otherwise, you would get some unexpected results.
The for loop is far more versatile than the other loop constructs; it can be constructed in unique ways that lessen the number of lines of code and improve the loop's efficiency. To demonstrate the versatility of the for loop, consider the following code:
int x = 1, z = 0; while (x <= 20) { z += x; x++; }
This loop simply adds the numbers from 1 to 20 (inclusive). The equivalent for loop is as follows:
int z = 0; for (int x=1; x <= 20; x++) { z+= x; }
To cut down the number of times the loop executes in half, we can use the following loop:
for (int x=1,y=20, z=0; x<=10 && y>10; x++, y--) { z+= x+y; }
To better understand how this loop works, we will break it up into its four main sections:
Mathematically speaking, the second for loop is not more efficient than the first (they both have the same order of magnitude). The purpose of this example, was simply to demonstrate the flexibility of the for loop.
How does the outcome of the next loop differ from the previous one?
for (int x=1,y=20, z=0; x<=10 && y>10; x++, y--, z+=x+y) { }
The break statement will allow you to exit a loop structure before the test condition is met. Once a break statement is encountered, the loop immediately terminates, skipping any remaining code. Here is an example:
int x = 0; while (x < 10){ System.out.println("Looping"); x++; if (x == 5) break; else //do something else }
In this example, the loop will stop executing when x equals 5.
The continue statement is used to skip the rest of the loop and continue execution at the next loop iteration.
for ( int x = 0 ; x < 10 ; x++){ if(x == 5) continue; //go back to beginning of loop with x=6 System.out.println("Looping"); }
This example will not print "Looping" if x is 5.
Conditional statements are used to provide your code with decision-making capabilities. There are two conditional structures in Java: the if-else statement, and the switch statement.
The syntax of an the if-else statement is as follows:
if (condition1) { //codeBlock 1 } else if (condition2) { //codeBlock 2 } else { //codeBlock 3 }
The if-else statement is typically made up of multiple blocks. Only one of the blocks will execute when the if-else statement executes (based on which of the conditions is true, of course). The if-else blocks and the else block are optional. Also, the if-else statement is not restricted to three blocks -it can contain as many of the if-else block as needed.
The following examples demonstrate the use of the if-else statement:
if ( x % 2 == 0) System.out.println("x is even"); else System.out.println("x is odd"); if (x = y) System.out.println("x equals y"); else if (x < y) System.out.println("x is less than y"); else System.out.println("x is greater than y");
In a way, the switch statement is a specialized version of the if-else statement. Here is the general syntax of the switch statement:
switch (expression){ case value1: codeBlock1; break; case value2: codeBlock2; break; default : codeBlock3; }
There are a number of points we need to highlight regarding the switch statement:
If a code block should execute when expression is one of multiple values, the values must be enumerated-each preceded with a case keyword and followed by a colon (we will see an example next)
Here is an example (assuming c is of type char):
switch (c){ case '1': case '3': case '5': case '7': case '9': System.out.println("c is an odd number"); break; case '0': case '2': case '4': case '6': case '8': System.out.println("c is an even number"); break; case ' ': System.out.println("c is a space"); break; default : System.out.println("c is not a number or a space"); }
The switch will evaluate c and jump to the case statement whose value is equal to c. If none of the case values equal c, the default section will be executed. Notice how multiple values can be used for each block.
What was covered in this chapter: