Chapter 3

The Java Language


CONTENTS


This chapter outlines the core syntax and constructs of the Java language. You will learn how to declare Java variables and write Java functions. You will see how Java's minimal set of primitive types can be combined with its rich object model to fulfill the goals of object-oriented programming. If you are an experienced C++ programmer, you will find many similarities between Java and C++. As you read this chapter, you will see many areas in which C++ has been improved upon and which will influence how you program, both in C++ and in Java.

Comments

Undoubtedly, you want to start your Java career off on the right foot, so let's dive right in and start with the important stuff-comments. Java supports three types of comment delimiters-the traditional /* and */ of C, the // of C++, and a new variant that starts with /** and ends with */.

The /* and */ delimiters are used to enclose text that is to be treated as a comment by the compiler. These delimiters are useful when you want to designate a lengthy piece of code as a comment, as shown in the following:

/* This is a comment that will span multiple
source code lines. */

The // comment delimiter is borrowed from C++ and is used to indicate that the rest of the line is to be treated as a comment by the Java compiler. This type of comment delimiter is particularly useful for adding comments adjacent to lines of code, as shown in the following:

Date today = new Date();      // create an object with today's date
System.out.println(today);    // display the date

Finally, the /** and */ delimiters are new to Java and are used to indicate that the enclosed text is to be treated as a comment by the compiler, but that the text is also part of the automatic class documentation that can be generated using JavaDoc. JavaDoc is fully described in Chapter 16, "Using JavaDoc to Document Your Program." These delimiters can be used to enclose multiple lines of text, identically to how /* and */ behave, as follows:

/** The NeuralNetwork class implements a back-propagation
network and ... */

The Java comment delimiters are summarized in Table 3.1.

Table 3.1. Java comment delimiters.
Start
End
Purpose
/* */The enclosed text is treated as a comment.
// (none)The rest of the line is treated as a comment.
/** */The enclosed text is treated as a comment by the compiler but is used by JavaDoc to automatically generate documentation.

Caution
You cannot nest comments in Java source code. Therefore, /* and */ appearing within a // comment are ignored as is the pattern // appearing within /* or /** comments. Comments cannot be placed within quoted strings, and if comment delimiters occur within a quoted string, they will be considered part of the quoted string.

Java Keywords

The following is a list of Java keywords:

Java Keywords
abstract floatpublic
boolean forreturn
break ifshort
byte implementsstatic
case importsuper
catch instanceofswitch
char intsynchronized
class interfacethis
continue longthrow
default nativethrows
do newtransient
double nulltry
else operatorvoid
extends packagevolatile
final privatewhile
finally protected  

Additionally, the Java specification reserves additional keywords that will be used in the future but are not part of Java 1.0. The following is a list of reserved Java keywords that are not currently used:

Reserved Java Keywords
byvalue genericouter
cast gotorest
const innervar
future operator 

Caution
You may have noticed that true and false are missing from the list of Java keywords. These are actually Boolean literals but can be thought of as keywords.

Primitive Types

Primitive types are the building blocks of the data portion of a language. Just as matter is composed of atoms clinging together, more complex data types can be made by combining a language's primitive data types. The Java language contains only a small set of primitive types: integer, floating-point, character, and Boolean.

In Java, as in C and C++, you declare a variable by giving its type followed by its name, as in the following examples:

int x;
float LifeRaft;
short people;
long TimeNoSee;
double amountDue, amountPaid;

In the preceding code, x is declared as an int (integer), LifeRaft is declared as a floating-point variable, people is declared as a short integer, TimeNoSee is declared as a long integer, and amountDue and amountPaid are declared as double-precision, floating-point values.

Integer Types

Java consists of four integer types: byte, short, int, and long, which are defined as 8-, 16-, 32-, and 64-bit signed values as summarized in Table 3.2.

Table 3.2. The Java integer primitive types.
TypeBit Size Minimum ValueMaximum Value
byte 8-256255
short 16-32,76832,767
int 32-2,147,483,6482,147,483,647
long 64-9,223,372,036,854,775,808 9,223,372,036,854,775,807

The operations that may be performed on integer primitives are shown in Table 3.3. A more detailed discussion of the Java operators is deferred until later in this chapter.

Table 3.3. Operators on integer primitives.
OperatorOperation
=Equality
!= Inequality
> Greater than
< Less than
>= Greater than or equal to
<= Less than or equal to
+Addition
-Subtraction
*Multiplication
/Division
%Modulus
++ Increment
-- Decrement
~Bitwise logical negation
& Bitwise AND
|Bitwise OR
^Bitwise XOR
<< Left shift
>> Right shift
>>> Right shift with zero fill

If either or both of the operands is of type long, then the result of the operation will be a 64-bit long. If either operand is not a long, it will be cast to a long prior to the operation. If neither operand is a long, then the operation will be performed with the 32-bit precision of an int. Any byte or short operands will be cast to int prior to the operation.

Caution
In Java, you cannot cast between an integer type and a Boolean type.

Floating-Point Types

Support for floating-point numbers in Java is provided through two primitive types-float and double, which are 32- and 64-bit values, respectively. The operators available for use on these primitives types are shown in Table 3.4.

Table 3.4. Operators on floating-point primitives.
Operator
Operation
=Equality
!= Inequality
> Greater than
< Less than
>= Greater than or equal to
<= Less than or equal to
+Addition
-Subtraction
*Multiplication
/Division
%Modulus
++ Increment
-- Decrement

Java floating-point numbers will behave as specified in IEEE Standard 754. Java variables of type float and double can be cast to other numeric types but cannot be cast to be of the boolean type.

If either or both of the operands is a floating-point type, the operation is considered to be a floating-point operation. If either of the operands is a double, then each will be treated as a double with the necessary casts being performed. If neither operand is a double, then each operand will be treated as a float and cast as necessary.

Floating-point numbers can take on any of the following values:

This last value, NaN, is used to indicate values that do not fit within the scale of negative infinity to positive infinity. For example, the following will produce a value of NaN:

0.0f / 0.0f

The inclusion of NaN as a floating-point value can cause some unusual effects when floating-point values are compared with the relational operators. Because NaN does not fit within the scale of negative infinity through positive infinity, comparing against it will always result in false. For example, both 5.3f > NaN and 5.3f < NaN are false. In fact, when NaN is compared to itself with ==, the result is false.

On the other hand, although negative and positive zero may sound like different values, comparing them with == will result in true.

Other Primitive Types

In addition to the integer and floating-point primitive types, Java includes two additional primitive types-Boolean and character. Variables of type boolean can hold either true or false, while variables of type char can hold a single Unicode character.

Caution
Remember, a Java Boolean variable is not a 1 or 0 in disguise as it is in other languages, in particular C and C++. Because of this, you cannot cast between Boolean and numeric types.

Default Values

One common source of programming errors is the use of an uninitialized variable. Frequently, this type of bug shows itself in a program that behaves erratically. Sometimes the program does what it's supposed to; other times it reformats your hard drive, overwrites your CMOS, declares war on a foreign country, or manifests some other undesirable side effect. It does this because an uninitialized variable may take on the value of whatever random garbage is in its memory location when the program runs. Java circumvents this problem, and possibly prevents World War III, by assigning a default value to any uninitialized variables. Default values are assigned based on the type of the variable, as shown in Table 3.5.

Table 3.5. Standard default values for Java primitive types.
PrimitiveDefault
byte 0
short 0
int 0
long 0L
float 0.0f
double 0.0d
char null
boolean false
all referencesnull

Note
It's certainly convenient and beneficial that Java will take care of assigning default values to uninitialized variables, but it is not wise to rely on this. Good programming practice suggests that you should initialize every variable you declare, without relying on default values. Although it is very unlikely that the default values would change (for example, the Boolean default of false is unlikely to change to true), other side effects are possible.
In a C program, I once spent hours tracking down a bug that was caused by my reliance on the compiler defaulting a global integer to 0. The compiler did its job correctly; unfortunately, another programmer saw my bad practice of using an uninitialized global and corrected it by initializing it for me-to 1. When I was reassigned to the maintenance of the program, I had no idea the change had been made.

Casting Between Primitive Types

Sometimes you have a variable that is of one type, and you want to use it as another. For example, one of the first programs I wrote was used to predict the final scores in baseball games based on a huge number of input statistics. It would come up with results like the Chicago Cubs beating the San Diego Padres with scores like 3.2 to 2.7. Since it was clearly impossible in real life to score a partial run, the results needed to be converted from floating-point to integer values. This is known as casting a variable. In Java, you can cast a variable of one type to another as follows:

float fRunsScored = 3.2f;
int iRunsScored = (int)fRunsScored;

In this case, the floating-point value 3.2 that is stored in fRunsScored will be cast into an integer and placed in iRunsScored. When cast into an integer, the non-whole portion of the fRunsScored will be truncated so that iRunsScored will equal 3.

This is an example of what is known as a narrowing conversion. A narrowing conversion may lose information about the overall magnitude or precision of a numeric value, as you saw in this case. You should always be careful when writing a narrowing conversion because of this potential for data loss.

The other type of conversion is called a widening conversion. A widening conversion may lose information about precision in the least significant bits of the value, but it will not lose information about the magnitude of the value. In general, widening conversions are much safer. Table 3.6 shows the widening conversions that are possible between Java primitive types.

Table 3.6. Available widening conversions among Java primitive types.
FromTo
byte short, int, long, float, or double
short int, long, float, or double
char int, long, float, or double
int long, float, or double
long float or double
float double

Literals

A literal is an explicit value that is used by a program. For example, your program may include a literal value of 3.1415 that is used whenever the value of pi is necessary, or it may include 65 as the mandatory retirement age. These values, 3.1415 and 65, are both literals.

Integer Literals

Integer literals can be specified in decimal, hexadecimal, or octal notation. To specify a decimal value, simply use the number as normal. To indicate that a literal value is a long, you can append either "L" or "l" to the end of the number. Hexadecimal values are given in base 16 and include the digits 0-9 and the letters A-F. To specify a hexadecimal value, use 0x followed by the digits and letters that comprise the value. Similarly, an octal value is identified by a leading 0 symbol.

For examples of specifying integer literals, see Table 3.7.

Table 3.7. Examples of integer literals.
IntegerLong OctalHexadecimal
00L 0 0x0
11L 01 0x1
10 10L012 0xA
15 15L017 0XF
16 16L020 0x10
100 100L0144 0x64

Floating-Point Literals

Similar to integer literals are Java's floating-point literals. Floating-point literals can be specified in either the familiar decimal notation (for example, 3.1415) or exponential notation (for example, 6.02e23). To indicate that a literal is to be treated as a single precision float, append either "f" or "F". To indicate that it is to be treated as a double precision value, append either "d" or "D".

Java includes predefined constants, POSITIVE_INFINITY, NEGATIVE_INFINITY, and NaN, to represent the infinity and not-a-number values.

The following list shows some valid floating-point literals:

43.3F
3.1415d
-12.123f
6.02e+23f
6.02e23d
6.02e-23f
6.02e23d

Boolean Literals

Java supports two Boolean literals-true and false.

Character Literals

A character literal is a single character or an escape sequence enclosed in single quotes, for example, 'b'. Escape sequences are used to indicate special characters or actions, such as line feed, form feed, or carriage return. The available escape sequences are shown in Table 3.8. For examples of character literals, consider the following:

'b'
'\n'
\u15e'
'\t'

Table 3.8. Escape sequences.
SequencePurpose
\b Backspace
\t Horizontal tab
\n Line feed
\f Form feed
\r Carriage return
\" Double quote
\' Single quote
\\ Backslash
\uxxxx Unicode character

String Literals

Although there is no string primitive type in Java, you can include string literals in your programs. Most applications and applets will make use of some form of string literal, probably at least for error messages. A string literal consists of zero or more characters (including the escape sequences shown in Table 3.8) enclosed in double quotes. As examples of string literals, consider the following:

"A String"
"Column 1\tColumn 2"
"First Line\r\nSecond Line"
"First Page\fSecond Page"
""

Because Java does not have a string primitive type, each use of a string literal causes an object of the String class to be created behind the scenes. However, because of Java's automatic memory management, your program doesn't need to do anything special to free or release the memory used by the literal or string once you are finished with it.

Arrays

In Java you declare an array using enclosing square bracket symbols ([]). For example, consider the following array declarations:

int intArray[];
float floatArray[];
double [] doubleArray;
char charArray[];

Notice that the brackets can be placed before or after the variable name. Placing the [] after the variable name follows the conventions of C, and if you are coming to Java from C or C++, you may want to continue that tradition. However, there is an advantage to placing the brackets before the variable name. By placing the brackets in front of the variable name, you can more easily declare multiple arrays. For example, consider the following declarations:

int [] firstArray, secondArray;
int thirdArray[], justAnInt;

On the first line both firstArray and secondArray are arrays. On the second line, thirdArray is an array but justAnInt is, as its name implies, a lone integer. The ability to declare singleton variables and arrays in the same statement, as on the second line in the preceding example, is the source of many problems in other programming languages. Java helps prevent this type of problem by providing an easy, alternative syntax for declaring arrays.

Allocation

Once an array is declared, it must be allocated. You probably noticed that the size of the arrays has not been specified in the examples so far. This is because, in Java, all arrays must be allocated with new. Declaring the following array would have resulted in a compile-time error:

int intArray[10];   // this is an error

To allocate an array you use new, as shown in the following examples:

int intArray[] = new int[100];
float floatArray[];
floatArray = new float[100];
long [] longArray = new long[100];
double [][] doubleArray = new double[10][10];

Initialization

An alternative way of allocating a Java array is to specify a list of element initializers when the array is declared. This is done as follows:

int intArray[] = {1,2,3,4,5};
char [] charArray = {'a', 'b', 'c'};
String [] stringArray = {"A", "Four", "Element", "Array"};

In this case, intArray will be a five-element array holding the values 1 through 5. The three-element array charArray will hold the characters 'a', 'b', and 'c'. Finally, stringArray will hold the strings shown.

Array Access

Items in a Java array are known as the components of the array. You can access a component at runtime by enclosing the component number you want to access with brackets as shown in the following:

int intArray[] = {100, 200, 300, 400, 500};

int a = intArray[0];        // a will be equal to 100
int b = intArray[1];        // b will be equal to 200
int c = intArray[2];        // c will be equal to 300
int d = intArray[3];        // d will be equal to 400
int e = intArray[4];        // e will be equal to 500

Java arrays are numbered from 0 to one less than the number of components in the array. Attempting to access an array beyond the bounds of the array (for example, intArray[42] in the preceding example) will result in a runtime exception, ArrayIndexOutOfBoundsException. Exception handling is discussed in detail in Chapter 22, "Exception Handling."

Caution
In many languages, especially C and C++, a string is really just an array of characters. This is not the case in Java. When working with Java strings, you should remind yourself that Java strings are instances of the Java String class and are not simply arrays of characters.

Operators

A language's operators can be used to combine or alter a program's values. Java contains a very rich set of operators. The complete list of Java operators is as follows:

A Complete List of Java Operators
=>< !~
?:== <=>=
|=&&|| ++--
+-* /&
|^% <<>>
>>>+=-= *=/=
&=|=^= %=<<=
>>=>>>=

Operators on Integers

The bulk of the Java operators work on integer values. The binary operators (those that require two operands) are shown in Table 3.9. The unary operators (those that require a single operand) are shown in Table 3.10. Each table gives an example of the use of each operator.

Table 3.9. Binary operators on integers.
OperatorOperation Example
=Assignment a = b
== Equalitya == b
!= Inequalitya != b
< Less thana < b
<= Less than or equal toa <= b
>= Greater than or equal toa >= b
> Greater thana > b
+Addition a + b
-Subtraction a - b
*Multiplication a * b
/Division a / b
%Modulus a % b
<< Left shifta << b
>> Right shifta >> b
>>> Right shift with zero filla >>> b
& Bitwise AND a & b
|Bitwise OR a | b
^Bitwise XOR a ^ b

Table 3.10. Unary operators on integers.
OperatorOperation Example
-Unary negation -a
~Bitwise logical negation ~a
++ Incrementa++ or ++a
-- Decrementa-- or --a

In addition to the operators shown in Tables 3.9 and 3.10, Java also includes an assortment of assignment operators that are based on the other operators. These operators will operate on an operand and store the resulting value back in the same operand. For example, to increase the value of a variable x, you could do the following:

x += 3;

This is equal to the more verbose x = x + 3. Each of the specialized Java assignment operators performs its normal function on the operand and then stores the value in the operand. The following assignment operators are available:

Integer Assignment Operators
+=-=*=
/=&=|=
^=%=<<=
>>=>>>=  

Operators on Floating-Point Values

The Java operators on floating-point values are a subset of those available to Java integer types. The operators that may operate on operands of type float and double are shown in Table 3.11, which also gives examples of their uses.

Table 3.11. Binary operators on integers.
OperatorOperation Example
=Assignment a = b
== Equalitya == b
!= Inequalitya != b
< Less thana < b
<= Less than or equal toa <= b
>= Greater than or equal toa >= b
> Greater thana > b
+Addition a + b
-Subtraction a - b
*Multiplication a * b
/Division a / b
%Modulus a % b
-Unary negation -a
++ Incrementa++ or ++a
-- Decrementa-- or --a

Operators on Boolean Values

The Java Boolean operators are summarized in Table 3.12. If you are coming to Java from a C or C++ background, you are probably already familiar with these. If not, however, the conditional operator will be a new experience.

Table 3.12. Operators on Boolean values.
OperatorOperation Example
!Negation !a
&& Conditional AND a && b
|| Conditional OR a || b
== Equalitya == b
!= Inequalitya != b
?: Conditionala ? expr1 : expr2

The conditional operator is Java's only ternary (three-operand) operator and has the following syntactic form:

booleanExpr ? expr1 : expr2

The value of booleanExpr is evaluated and if true, the expression expr1 is executed; if false, expression expr2 is executed. This makes the conditional operator a convenient shorthand for the following:

if(booleanExpression)
    expr1
else
    expr2

Controlling Your Program

The Java keywords for controlling program flow are nearly identical to C and C++. This is one of the most obvious ways in which Java shows its legacy as a derivative of these two languages. In this section, you will see how to use Java's control flow commands to write methods.

Selection

The Java language provides two alternative structures-if statements and switch statements-for selecting among alternatives. Although it would be possible to spend your entire Java programming career using only one of these at the expense of the other, each has its definite advantages.

The if Statement

A Java if statement is a test of any Boolean expression. If the Boolean expression evaluates to true, the statement following the if is executed. On the other hand, if the Boolean expression evaluates to false, the statement following the if is not executed. For example, consider the following code fragment:

import java.util.Date;
Date today = new Date();
if (today.getDay == 0) then
    System.out.println("It is Sunday.");

This code uses the java.Util.Date package and creates a variable named today that will hold the current date. The getDay member method is then applied to today and the result compared to 0. A return value of 0 for getDay indicates that the day is Sunday, so if the Boolean expression today.getDay == 0 is true, a message is displayed. If today isn't Sunday, no action occurs.

If you are coming to Java from a C or C++ background, you may have been tempted to rewrite the preceding example as follows:

import java.util.Date;
Date today = new Date();
if (!today.getDay) then
    System.out.println("It is Sunday.");

In C and C++, the expression !today.getDay would evaluate to 1 whenever today.getDay evaluated to 0 (indicating Sunday). In Java, the expression used within an if statement must evaluate to a Boolean. Therefore, this code doesn't work because !today.getDay will evaluate to 0 or 1, depending on which day of the week it is. And, as you learned earlier in this chapter, integer values cannot be cast to Boolean values. This is, of course, an example where Java's nuances may take a little getting used to for C and C++ programmers. Once you're accustomed to the change, however, you will find your code more readable, reliable, and maintainable.

Of course, an if statement without an else is as incomplete as a Labrador Retriever without a bandanna around his neck. Not wanting to be accused of cruelty to animals or programmers, the Java developers included an else statement that can be executed whenever an if statement evaluates to false. This can be seen in the following sample code:

import java.util.Date;
Date today = new Date();
if (today.getDay == 0) then
    System.out.println("It is Sunday.");
else
    System.out.println("It is NOT Sunday.");

In this case, the same message will be displayed whenever it is Sunday, but a different message will be displayed whenever it is not Sunday. Both examples so far have only shown the execution of a single statement within the if or the else cases. By enclosing the statements within curly braces, you can execute as many lines of code as you'd like. This can be seen in the following example that makes some suggestions about how to spend each day of the week:

import java.util.Date;
Date today = new Date();
if (today.getDay == 0) then {
    System.out.println("It is Sunday.");
    System.out.println("And a good day for golf.");
}
else {
    System.out.println("It is NOT Sunday.");
    System.out.println("But still a good day for golf.");
}

Because it's possible to execute whatever code you desire in the else portion of an if…else block, you may have already reasoned that it is possible to execute another if statement inside the else statement of the first if statement. This is commonly known as an if…else if…else block, an example of which follows:

import java.util.Date;
Date today = new Date();
if (today.getDay == 0) then
    System.out.println("It is Sunday.");
else if (today.getDay == 1) then
    System.out.println("It is Monday.");
else if (today.getDay == 2) then
    System.out.println("It is Tuesday.");
else if (today.getDay == 3) then
    System.out.println("It is Wednesday.");
else if (today.getDay == 4) then
    System.out.println("It is Thursday.");
else if (today.getDay == 5) then
    System.out.println("It is Friday.");
else
    System.out.println("It must be Saturday.");

The switch Statement

As you can see from the previous code sample, a lengthy series of if…else if…else statements can get convoluted and hard to read as the number of cases increases. Fortunately, you can avoid this problem by using Java's switch statement. Like its C and C++ cousins, the Java switch statement is ideal for testing a single expression against a series of possible values and executing the code associated with the matching case statement, as shown in the following example:

import java.util.Date;
Date today = new Date();
switch (today.getDay) {
    case 0:    // Sunday
        System.out.println("It is Sunday.");
        break;
    case 1:    // Monday
        System.out.println("It is Monday.");
        break;
    case 2:    // Tuesday
        System.out.println("It is Tuesday.");
        break;
    case 3:    // Wednesday
        System.out.println("It is Wednesday.");
        break;
    case 4:    // Thursday
        System.out.println("It is Thursday.");
        break;
    case 5:    // Friday
        System.out.println("It is Friday.");
        System.out.println("Have a nice weekend!");
        break;
    default:   // Saturday
        System.out.println("It must be Saturday.");
}
System.out.println("All done!");

You should have noticed that each day has its own case within the switch. The Saturday case (where today.getDay = 6) is not explicitly given but is instead handled by the default case. Any switch block may include an optional default case that will handle any values not caught by an explicit case.

Within each case, there can be multiple lines of code. The block of code that will execute for the Friday case, for example, contains three lines. The first two lines will simply display informational messages, but the third is a break statement. The keyword break is used within a case statement to indicate that the flow of the program should move to the first line following the switch block. In this example, break appears as the last statement in each case except the default and will cause program execution to move to the line that prints "All done!" The break statement was left out of the default block because by that point in the code, the switch block was ending, and there was no point in using an explicit command to exit the switch.

If, as the previous example seems to imply, you always need to include a break statement at the end of each block, why not just leave break out and have Java assume that after a block executes, control should move outside the switch block? The answer is that there are times when you do not want to break out of the switch statement after executing the code for a specific case value. For example, consider the following code that could be used as a scheduling system for physicians:

import java.util.Date;
Date today = new Date();
switch (today.getDay) {
    case 0:      // Sunday
    case 3:      // Wednesday
    case 6:      // Saturday
        System.out.println("It's Golf Day!");
        break;
    case 2:      // Tuesday
        System.out.println("Tennis at 8:00 am");
    case 1:      // Monday
    case 4:      // Thursday
    case 5:      // Friday
        System.out.println("Office Hours: 10:00 - 5:00");
        break;
}
System.out.println("All done!");

This example illustrates a couple of key concepts about switch statements. First, you'll notice that it is possible to have multiple cases execute the same block of code, as follows:

case 0:      // Sunday
case 3:      // Wednesday
case 6:      // Saturday
    System.out.println("It's Golf Day!");
    break;

This code will result in the message "It's Golf Day" being displayed if the current day is Wednesday, Saturday, or Sunday. If you collect the three cases together without any intervening break statements, each will execute the same code. But consider what happens on Tuesday when the following code executes:

case 2:      // Tuesday
    System.out.println("Tennis at 8:00 am");

Certainly a reminder about the message match will be displayed, but this case doesn't end with a break statement. Because Tuesday's code doesn't end with a break statement, the program will continue executing the code in the following cases until a break is encountered. This means that Tuesday's code flows into the code used for Monday, Thursday, and Friday as shown in the following:

case 2:      // Tuesday
    System.out.println("Tennis at 8:00 am");
case 1:      // Monday
case 4:      // Thursday
case 5:      // Friday
    System.out.println("Office Hours: 10:00 - 5:00");
    break;

This will result in the following messages being displayed every Tuesday:

Tennis at 8:00 am
Office Hours: 10:00 - 5:00

On Monday, Thursday, and Friday, only the latter message will display.

In addition to writing switch statements that use integer cases, you can use character values as shown in the following example:

switch (aChar) {
    case 'a':
    case 'e':
    case 'i':
    case 'o':
    case 'u':
        System.out.println("It's a vowel!");
        break;
    default:
        System.out.println("It's a consonant!");
}

Iteration

Iteration is an important concept in any computer language. Without the ability to loop or iterate through a set of values, our ability to solve real-world problems would be severely limited. Java's iteration statements are nearly identical to those found in C and C++ and include for loops, while loops, and do…while loops.

The for Statement

If Java programmers turn out to be anything like C or C++ programmers, many of them will be partial to the for statement because of its syntactic elegance. The first line of a for loop enables you to specify a starting value for a loop counter, specify the test condition that will exit the loop, and indicate how the loop counter should be incremented after each pass through the loop. This is definitely a statement that offers a lot of bang for the buck. The syntax of a Java for statement is as follows:

for (initialization; testExpression; incremement)
    statement

For example, a sample for loop may appear as follows:

int count;
for (count=0; count<100; count++)
    System.out.println("Count = " + count);

In this example, the initialization statement of the for loop sets count to 0. The test expression, count < 100, indicates that the loop should continue as long as count is less than 100. Finally, the increment statement increments the value of count by one. As long as the test expression is true, the statement following the for loop setup will be executed, as follows:

System.out.println("Count = " + count);

Of course, you probably need to do more than one thing inside the loop. This is as easy to do as using curly braces to indicate the scope of the for loop, as shown in the following:

int count;
for (count=0; count<100; count++) {
    YourMethod(count);
    System.out.println("Count = " + count);
}

One nice shortcut that can be taken with a Java for loop is to declare and initialize the variable used in the loop. For example, in the following code, the variable count is declared directly within the for loop:

for (int count=0; count<100; count++)
    System.out.println("Count = " + count);

It may look like an inconsequential difference whether you declare a variable before a for loop or within the loop. However, there are advantages to declaring the variable within the loop. First, it makes your intention to use the variable within the loop clear. If the variable is declared above the for loop, how will you remember (and how will future programmers know) that the variable was intended for use only within the loop? Second, a variable declared within the for loop will go out of scope at the end of the loop. This means you could not write the following code:

for (int count=0; count<100; count++)
    System.out.println("Count = " + count);
System.out.println("Loop exited with count = " + count);

The last line cannot find a variable named count because count goes out of scope when the for loop terminates. This means that, in addition to making the intended purpose of the variable more clear, it is also impossible to accidentally bypass that intent and use the variable outside the loop.

You can also leave out portions of the first line of a for loop. In the following example, the increment statement has been left out:

for (int count=0; count<100; ) {
    count += 2;
    System.out.println("Count = " + count);
}

Of course, leaving the increment statement out of the for loop declaration in this example doesn't achieve any useful purpose since count is incremented inside the loop.

It is possible to get even fancier with a Java for loop by including multiple statements or conditions. For example, consider the following code:

for (int up=0, down = 20; up < down; up++, down -= 2 ) {
    System.out.println("Up = " + up + "\tDown = " + down);
}

This loop starts the variable up at 0 and increments it by 1. It also starts the variable down at 20 and decrements it by 2 for each pass through the loop. The loop continues until up has been incremented enough that it is equal to or greater than the variable down.

The test expression portion of a Java for loop can be any Boolean expression. Because of this, it does not need to be a simple test (x < 10), as shown in the preceding examples. The test expression can be a method call, a method call combined with a value test, or anything that can be phrased as a Boolean expression. For example, suppose you want to write a method that will display a message indicating the first year since World War II that the Chicago Cubs appeared in the World Series. You could do this as follows:

public boolean DidCubsPlayInWorldSeries(int year) {
    boolean retval;

    switch(year) {
        case 1907:             // these are years the Cubs won
        case 1908:
            retval = true;
            break;
        case 1906:             // these are years the Cubs lost
        case 1910:
        case 1918:
        case 1929:
        case 1932:
        case 1935:
        case 1938:
        case 1945:
            retval = true;
            break;
        default:
            retval = false;
    }
    return retval;
}

public void FindFirstAfterWWII() {
    for (int year=1946; DidCubsPlayInWorldSeries(year)==false; year++) {
        System.out.println("The Cubs didn't play in " + year);
    }
}

The method DidCubsPlayInWorldSeries is passed an integer value indicating the year and returns a Boolean value that indicates whether or not the Cubs made it to the World Series in that year. This method is an example of the switch statement shown earlier in this chapter.

The method FindFirstAfterWWII uses a for loop to find a year in which the Cubs played in the World Series. The loop starts year with 1946 and increments year by one for each pass through the loop. The test expression for the loop will allow the loop to continue as long as the method DidCubsPlayInWorldSeries returns false. This is a useful example because it shows that a method can be called within the test expression of a for loop. Unfortunately, it is a bad example in that the Cubs haven't won the World Series since the goose step was popular in Berlin, and there is no sign of that changing in the near future. In other words, a loop that looks for a Cubs World Series appearance after 1945 is an infinite loop.

The while Statement

Related to the for loop is the while loop. The syntax for a while loop is as follows:

while (booleanExpression)
    statement

As you can tell from the simplicity of this, the Java while loop does not have the built-in support for initializing and incrementing variables that its for loop does. Because of this, you need to be careful to initialize loop counters prior to the loop and increment them within the body of the while loop. For example, the following code fragment will display a message five times:

int count = 0;
while (count < 5) {
    System.out.println("Count = " + count);
    count++;
}

The do…while Statement

The final looping construct in Java is the do…while loop. The syntax for a do…while loop is as follows:

do {
    statement
} while (booleanExpression);

This is similar to a while loop except that a do…while loop is guaranteed to execute at least once. It is possible that a while loop may not execute at all depending on the test expression used in the loop. For example, consider the following method:

public void ShowYears(int year) {
    while (year < 2000) {
        System.out.println("Year is " + year);
        year++;
    }
}

This method is passed a year value, then loops over the year displaying a message as long as the year is less than 2000. If year starts at 1996, then messages will be displayed for the years 1996, 1997, 1998, and 1999. However, what happens if year starts at 2010? Because the initial test, year < 2000, will be false, the while loop will never be entered. Fortunately, a do…while loop can solve this problem. Because a do…while loop performs its expression testing after the body of the loop has executed for each pass, it will always be executed at least once. This is a very valid distinction between the two types of loop, but it can also be a source of potential errors. Whenever you use a do…while loop, you should be careful to consider the first pass through the body of the loop.

Jumping

Of course, it is not always easy to write all of your for, while and do…while loops so that they are easy to read and yet the loops terminate on exactly the right pass through the loop. Java makes it easier to jump out of loops and to control other areas of program flow with its break and continue statements.

The break Statement

Earlier in this chapter, you saw how the break statement is used to exit a switch statement. In a similar manner, break can be used to exit a loop. This can be seen in Figure 3.1.

Figure 3.1 : Flow of control with a break statement.

As Figure 3.1 illustrates, if the break statement is encountered, execution will continue with statement4. As an example of this, consider the following code:

int year = 1909;
while (DidCubsWinTheWorldSeries(year) == false) {
    System.out.println("Didn't win in " + year);
    if (year >= 3000) {
        System.out.println("Time to give up. Go White Sox!");
        break;
    }
}
System.out.println("Loop exited on year " + year);

This example shows a while loop that will continue to execute until it finds a year that the Chicago Cubs won the World Series. Because they haven't won since 1908 and the loop counter year starts with 1909, it has a lot of looping to do. For each year they didn't win, a message is displayed. However, even die-hard Cubs fans will eventually give up and change allegiances to the Chicago White Sox. In this example, if the year is 3000 or later, a message is displayed and then a break is encountered. The break statement will cause program control to move to the first statement after the end of the while loop. In this case, that will be the following line:

System.out.println("Loop exited on year " + year);

The continue Statement

Just as a break statement can be used to move program control to immediately after the end of a loop, the continue statement can be used to force program control back to the top of a loop. This can be seen in Figure 3.2.

Figure 3.2 : Flow of control with a continue statement.

Suppose you want to write a method that will count and display the number of times the Cubs have won the World Series this century. One way to do this would be to first see if the Cubs played in the World Series and then see if they won. This could be done as follows:

int timesWon = 0;
for (int year=1900; year <= 2000; year++) {
    if (DidCubsPlayInWorldSeries(year) = false)
        continue;
    if (DidCubsWinWorldSeries(year)) {
        System.out.println("Cubbies won in " + year + "!");
        timesWon++;
    }
}
System.out.println("The Cubs won " + timesWon + " times.");

In this case, a for loop is used to iterate through the years from 1900 to 2000. The first line within the loop tests to see if the Cubs played in the World Series. If they didn't, the continue statement is executed. This moves program control back to the for loop. At that point, year is incremented and the expression year <= 2000 is retested. If year is less than or equal to 2000, the loop continues. If, however, DidCubsPlayInWorldSeries equals true, then the continue statement is skipped, and the next test is performed to see if the Cubs won that year.

Using Labels

Java does not include a goto statement. However, the fact that goto is a reserved word indicates that it may be added in a future version. Instead of goto, Java allows you to combine break and continue with a label. This has an effect similar to a goto in that it allows a program to reposition control. In order to understand the use of labels with break and continue, consider the following example:

public void paint(Graphics g) {
    int line=1;

    outsideLoop:
    for(int out=0; out<3; out++) {
        g.drawString("out = " + out, 5, line * 20);
        line++;

        for(int inner=0;inner < 5; inner++) {
            double randNum = Math.random();
            g.drawString(Double.toString(randNum), 15, line * 20);
            line++;
            if (randNum < .10) {
                g.drawString("break to outsideLoop", 25, line * 20);
                line++;
                break outsideLoop;
            }
            if (randNum < .60) {
                g.drawString("continue to outsideLoop", 25, line * 20);
                line++;
                continue outsideLoop;
           }
        }
    }
    g.drawString("all done", 50, line * 20);
}

This example includes two loops. The first loops on the variable out, and the second loops on the variable inner. The outer loop has been labeled by the following line:

outsideLoop:

This statement will serve as a placeholder and as a name for the outer loop. A random number between 0 and 1 is generated for each iteration through the inner loop. This number is displayed on the screen. If the random number is less than 0.10, the statement break outsideLoop is executed. A normal break statement in this position would break out of the inner loop. However, since this is a labeled break statement, it has the effect of breaking out of the loop identified by the name. In this case, program control passes to the line that displays "all done" since that is the first line after outsideLoop.

On the other hand, if the random number is not less than 0.10, the number is compared to 0.60. If it is less than this, the statement continue outsideLoop is executed. A normal, unlabeled continue statement at this point would have the effect of transferring program control back to the top of the inner loop. Because this is a labeled continue statement, program control is transferred to the start of the named loop. A sample run of this method, as captured in the Java Applet Viewer, is shown in Figure 3.3.

Figure 3.3 : Sample results demonstrating the use of labels.

As you can see in Figure 3.3, the first pass through the outer loop resulted in four passes through the inner loop. When the value 0.518478 was generated, it caused the continue outsideLoop to execute because the number is less than 0.60. The next pass through the outer loop was similar except that it did a continue of the outer loop after only one iteration through the inner loop. Finally, on the third pass through the outer loop, the program generated a value lower than 0.10, which caused the program to break to the outer loop. You can see that, at this point, the next line of code to be executed was the first line of code after the outer loop (the line that prints the message "all done").

Java Classes

Now that you've seen most of the low-level details of the Java language, it's time to turn your attention to Java classes and see how Java is able to live up to its claim of being an object-oriented language. A Java class is a compile-time concept that represents a runtime object. In other words, a class is a definition or template for an object that will exist within the program. For example, if you have a class called Car, you may have a particular instance of that class that is a 1966 Volkswagen Beetle. The instances (1966 Volkswagen Beetle) of a class (Car) are known as objects. In order to define a class in Java, you would do something similar to the following:

class Car {
    // member variables
    // member methods
}

Field Declarations

Car is now an empty class. In order to make it usable and useful, you need to add some fields to the class. A field can be either a member variable or a member method. To declare member variables, all you need to do is identify the variable by type and name in the class definition, as shown in the following:

class Car {
    // these are member variables
    String manufacturer;
    String model;
    int year;
    int passengers;
}

In this example, Car has been extended to include String variables for manufacturer and model, and integer variables for the year it was built and the number of passengers it can hold. From this class definition, it is then possible to create instances, or objects, at runtime, as shown in Figure 3.4.

Figure 3.4 : The Car class and objects.

Field Access

One of the principal advantages of object-oriented programming is encapsulation. Encapsulation is the ability to define classes that hide their implementation details from other classes, exposing only their public interfaces to those other classes. Support for encapsulation in Java comes from three keywords: public, private, and protected. When you are defining a class, these field access modifiers are used to control who has access to each field in the class. By declaring a field as public, you are indicating that it is entirely accessible to all other classes. Continuing with the Car example, to declare all of the fields as public, do the following:

class Car {
    public String manufacturer;
    public String model;
    public int year;
    public int passengers;
}

Of course, declaring everything as public doesn't exactly achieve the goal of encapsulation because it lets other classes directly access variables in the Car class. Consider what would happen if you needed to create an instance of this class for a 1964-and-a-half Mustang. Because year only holds integer values, it would have to be changed to a float so that it could hold 1964.5. If code in other classes directly accessed year, that code could conceivably break.

To restrict access to a field, use the keyword private. A class cannot access the private fields of another class. Suppose the Car class is intended for use in a used car sales application. In this case, you may want to define Car as follows in order to hide your cost for a car from potential buyers:

class Car {
    public String manufacturer;
    public String model;
    public int year;
    public int passengers;
    private float cost;
}

Finally, the keyword protected is used to indicate that fields are accessible within the current class and all classes derived from the class, but not to other classes. The ability to derive a class from another class will be discussed later in this chapter.

Setting Initial Values

One extremely nice aspect of Java class declarations that is a deviation from C++ is the ability to specify initial values for member variables in the variable declaration. For example, because most cars will hold four passengers, it may be reasonable to default the passengers member variable to 4, as shown in the following code:

class Car {
    public String manufacturer;
    public String model;
    public int year;
    public int passengers = 4;
    private float cost;
}

Static Members

In addition to private, protected, and public members, a Java class can also have static members. A static member is one that belongs to the class itself, not to the instances of the class. Regardless of how many instances of a class have been created by a program at runtime, there will exist exactly one instance of each static member. Declaring a static member is done by adding the keyword static to any of the other field access modifiers, as shown in the following:

class Car {
    public String manufacturer;
    public String model;
    public int year;
    public int passengers = 4;
    private float cost;
    public static int tireQty = 4;
}

In this case, the variable tireQty has been added and is set to 4. Because every car will have four tires, tireQty was declared as static. Also, because we want tireQty to be accessible to other classes, it has been declared public.

It is also possible to declare member methods as static, as will be shown later in this chapter.

Member Methods

In addition to member variables, most classes will also have member methods. Because member methods, like member variables, are fields, access to them can be controlled with the public, protected, and private modifiers. A member method is declared according to the following syntax, in which elements enclosed in square brackets "[…]" are optional:

[methodModifiers] resultType methodName [throws exceptionList] {
    // method body
}

The methodModifiers are the familiar public, protected, and private keywords you've already seen as well as some additional modifiers. The method modifiers are described in Table 3.13.

Table 3.13. Method modifiers.
ModifierPurpose
public Accessible outside the class in which it is declared.
Protected Accessible by the class in which it is declared and by subclasses of that class.
Private Accessible only by the class in which it is declared.
Static A method of the class rather than of a particular instance of the class.
Abstract Not implemented in this class.
Final Cannot be overridden in subclasses.
Native A platform-dependent implementation of the method in another language, typically C or assembly.
Synchronized Used to indicate a critical method that will lock the object to prevent execution of other methods while the synchronized method executes.

The resultType of a method declaration can be one of the primitive types (for example, int, float, char), another class, or void. A resultType of void indicates that no result is passed back to the caller of the method. After the method name is given, a list of exceptions throwable by the method is given. If no exceptions are thrown by the method, this list is not necessary. Exception handling is discussed in full in Chapter 22.

As an example of adding a method to the Car class, consider the following sample code:

class Car {
    public String manufacturer;
    public String model;
    public int year;
    public int passengers;
    public float CalculateSalePrice() {
        return cost * 1.5;
    }
    private float cost;
}

In this case, the Car class has had a public member method, CalculateSalePrice, added. The method returns a float, and the body of the method calculates this return value. To calculate the sale price of a car, the private member variable cost is multiplied by 1.5, reflecting a markup of 50% over the amount the car was purchased for.

Overloaded Methods

The ability to overload methods is one of the biggest advantages to working in an object oriented language, and Java certainly doesn't disappoint. Overloading a method means to use the same method name for more than one method. For example, the Car class can include two CalculateSalePrice methods, as follows:

public float CalculateSalePrice() {
    return cost * 1.5;
}

public float CalculateSalePrice(double margin) {
    return cost * (1 + margin);
}
private float cost;

In this case, the first version of CalculateSalePrice is not passed any parameters and bases the sale price on the cost plus 50% (cost * 1.5). The second version is passed a margin by which the car should be marked up in determining the car's sale price.

At runtime, Java is able to distinguish between these methods by the parameters passed to each. Because of this you can overload a method as many times as you want as long as the parameter lists of each version are unique. In other words, you could not do the following:

public float CalculateSalePrice() {
    return cost * 1.5;
}

public float CalculateSalePrice(double margin) {
    return cost * (1 + margin);
}

// this method declaration conflicts with the preceding method
public float CalculateSalePrice(double multiplier) {
    return cost * margin;
}
private float cost;

In this situation, the last two declarations are in conflict because each is passed a double. Different parameter names are insufficient to distinguish between two versions of the same overloaded function. They must differ by at least one parameter type.

Constructors

A special type of member method is known as a constructor. A constructor is used to create new instances of a class. You can identify a constructor because it will have the same name as the class. Like any other method, a constructor can be overloaded as long as the versions are distinguishable by the parameter types passed to each. Typically, a constructor will set the member variables of an object to values appropriate for that instance. As an example, consider the following variation on the Car class:

public class Car {
    String manufacturer;
    String model;
    int year;
    int passengers;
    float cost;

    // calculate the sale price of a car based on its cost
    public double CalculateSalePrice() {
        return cost * 1.5;
    }

    // a public constructor
    public Car(String madeBy, String name, int yr, int pass,
            float cst) {
        manufacturer = madeBy;
        model = name;
        year = yr;
        passengers = pass;
        cost = cst;
    }

    // create and return a string with the basic details about
    // this particular car
    public String GetStats() {
        return new String(year + " " + manufacturer + " " + model);
    }
}

A constructor, Car, has been added to this version of the Car class. The constructor is passed five parameters that will be used as initial values for the instance variables manufacturer, model, year, passengers, and cost. The code for the constructor simply sets the five instance variables. The Car class has also received a new public member, GetStats, that creates a string that contains the basic facts about the car. By using the constructor and the new GetStats method, you can now display some information about a car. For example, the following code will display "1967 VW Bug":

Car myCar = new Car("VW", "Bug", 1967, 4, 3000);
String str = myCar.GetStats();
System.out.println(str);

The new instance of the class Car was created with the following line:

Car myCar = new Car("VW", "Bug", 1967, 4, 3000);

The use of the Java keyword new instructs Java to create a new object of type Car by allocating memory for it and to invoke the constructor for Car whose signature matches the parameter list. In this case, Car has only one constructor, so it is invoked and will set the instance variables to the values of the parameters. Once the variable myCar goes out of scope at the end of the function in which it is declared, the automatic memory management features of Java will detect that the memory that was allocated by new is no longer referenced and it will be released.

Tip
If a class does not specifically include a constructor, Java will provide a default constructor that takes no parameters. This constructor will allow you to create new instances of a class and will set all member variables to their Java system default values. However, it is a dangerous and unwise practice to rely on the existence of a Java default constructor. In general, you should always provide at least one constructor for each class you define.

The this Variable

All Java classes contain a hidden member variable named this. The this member can be used at runtime to reference the object itself. One excellent use of this is in constructors. It is very common to have a set of instance variables in a class that must be set to values that are passed to a constructor. When you are doing this, it would be nice to have code that was similar to the following:

year = year;

Ideally the variable on the left could be the instance variable, and the variable on the right could be the parameter passed to the constructor. Unfortunately, I don't know of any languages that would be able to make this distinction. The typical solution most programmers have settled on is similar to the following:

public class Car {
    String manufacturer;
    String model;
    int year;
    int passengers;

    // a public constructor
    public Car(String madeBy, String name, int yr, int pass,
            float cst) {
        manufacturer = madeBy;
        model = name;
        year = yr;
        passengers = pass;
        cost = cst;
    }
}

Here, we've had to come up with two names for each concept: the best variable names (manufacturer, model, and so on) are used as the instance variables in the class declaration. The less satisfactory names are passed as parameters so as to distinguish them from the instance variables. The assignment statements are then very readable by Java but seem a little contrived to human readers. Java's this keyword provides a very effective solution to this problem in that the constructor can be written as follows:

public class Car {
    String manufacturer;
    String model;
    int year;
    int passengers;
    float cost;

    // calculate the sale price of a car based on its cost
    public double CalculateSalePrice() {
        return cost * 1.5;
    }

    // a public constructor
    public Car(String manufacturer, String model, int year,
            int passengers, float cost) {
        this.manufacturer = manufacturer;
        this.model = model;
        this.year = year;
        this.passengers = passengers;
        this.cost = cost;
    }
}

In this case, the variables like this.year refer to the instance variables, whereas the unqualified variables like year refer to the constructor's parameters.

Of course, this is only one example of how you can use this. It is also frequently used as a parameter to other functions from within member methods.

Class Inheritance

In Java, every class you declare will be derived from another class. You can specify the class to derive from by using the extends keyword as follows:

public class ClassicCar extends Car {
    // member methods and variables
}

As you probably noticed, extends was left out of all the prior examples in this chapter. This is because if a class is not declared as being derived from a specific class, then it is assumed to be derived from the Java base class, Object. This means that the following two class declarations are equivalent:

public class Car {
    // member methods and variables
}

public class Car extends Object {
    // member methods and variables
}

Because Object is the class from which all other Java classes are ultimately derived, this provides a common set of functionality among all Java classes. Most notably, garbage collection is possible because all classes will ultimately trace their lineage back to Object as shown in Figure 3.5.

Figure 3.5 : Everything is (eventually) derived from object.

A derived class is commonly referred to as a subclass, while the class it is derived from is commonly referred to as a superclass. The term immediate superclass is used to describe the class from which a subclass is directly derived. In Figure 3.5, for example, ClassicCar is a subclass of both Car and Object. Car and Object are both superclasses of ClassicCar, but only Car is the immediate superclass of ClassicCar.

Overriding Member Methods

When you create a subclass, you inherit all of the functionality of its superclass, and then you can add or change this functionality as desired. As an example of this, consider the altered declaration of a Car class in the following code:

public class Car {
    private int year;
    private float originalPrice;

    // calculate the sale price of a car based on its cost
    public double CalculateSalePrice() {
        double salePrice;
        if (year > 1994)
            salePrice = originalPrice * 0.75;
        else if (year > 1990)
            salePrice = originalPrice * 0.50;
        else
            salePrice = originalPrice * 0.25;
        return salePrice;
    }

    // a public constructor
    public Car(int year, float originalPrice) {
        this.year = year;
        this.originalPrice = originalPrice;
    }
}

This version of the Car class holds information about the year and the original purchase price of the car. It has a member method, CalculateSalePrice, that determines the price for which to sell the car based on its age. Depending upon the age of the car, it can sell for either 75%, 50%, or 25% of its original price.

Although very simplistic, this is a good start for most cars. However, it is completely inadequate for classic, old cars. This algorithm would indicate that a 1920 Model T would be worth only 25% of its original 1920 price. A slight improvement on this would be to assume that every ClassicCar is worth $10,000. To do this, ClassicCar is derived from Car, as follows:

public class ClassicCar extends Car {
    // calculate the sale price of a car based on its cost
    public double CalculateSalePrice() {
        return 10000;
    }

    // a public constructor
    public ClassicCar(int year, float originalPrice) {
        super(year, originalPrice);
    }
}

Because ClassicCar is derived from Car, it inherits all of the functionality of Car, including its member variables year and originalPrice. The function CalculateSalePrice appears in both class declarations. This means that the occurrence of this function in ClassicCar overrides the occurrence of it in Car for object instances of ClassicCar. As an example of how this works, consider the following:

ClassicCar myClassic = new ClassicCar(1920, 1400);
double classicPrice = myClassic.CalculateSalePrice();

Car myCar = new Car(1990, 12000);
double price = myCar.CalculateSalePrice();

The variable myClassic is of type ClassicCar and is constructed using that class's constructor, which is passed an original price for the car of $1,400. The sale price of this car is calculated and stored in classicPrice. Because myClassic is a ClassicCar, the sale price will be $10,000. Next, myCar is constructed as a new object of type Car with an original cost of $12,000. Its sale price is determined and stored in price. Because myCar is a Car, its sale price will be based on the year it was made (1990) and will be 25% of $12,000, or $3,000.

The super Variable

In the preceding declaration for ClassicCar, you may have noticed that the constructor made use of a variable named super. Just as each object has a this variable that references itself, each object (other than those of type Object itself) has a super variable that represents the parent class. In this case, super(year, originalPrice) invokes the constructor of the superclass Car.

Class Modifiers

Classes that are created in Java can be modified by any of three class modifiers. The Java class modifiers are public, final, and abstract. If no class modifier is used, then the class may only be used within the package in which it is declared. A public class is a class that can be accessed from other packages. A class that is declared as final cannot be derived from, meaning it cannot have subclasses.

Abstract Classes

Sometimes you may want to declare a class and yet not know how to define all of the methods that belong to that class. For example, you may want to declare a class called Mammal and include in it a member method called MarkTerritory. However, you don't know how to write MarkTerritory because it is different for each type of Mammal. Of course, you plan to handle this by deriving subclasses of Mammal, such as Dog and Human. But what code do you put in the MarkTerritory function of Mammal itself?

In Java you can declare the MarkTerritory function of Mammal as an abstract method. Doing so allows you to declare the method without writing any code for it in that class. However, you can write code for the method in the subclass. If a method is declared abstract, then the class must also be declared as abstract. For Mammal and its subclasses, this means they would appear as follows:

abstract class Mammal {
    abstract void MarkTerritory();
}

public class Human extends Mammal {
    public void MarkTerritory() {
        // mark territory by building a fence
    }
}

public class GangMember extends Mammal {
    public void MarkTerritory() {
        // mark territory with graffiti
    }
}

public class Dog extends Mammal {
    public void MarkTerritory() {
        // mark territory by doing what dogs do
    }
}

With the preceding declarations, the Mammal class contains no code for MarkTerritory. The Human class could contain code that would mark territory by building a fence around it, while the GangMember class could contain code that would mark territory by spray-painting graffiti. The Dog class would mark territory by raising the dog's leg and doing what dogs do to mark territory.

Note
A method that is private or static cannot also be declared abstract. Because a private method cannot be overridden in a subclass, a private abstract method would not be usable. Similarly, because all static methods are implicitly final, static methods cannot be overridden.

Implementing Interfaces

Typically, an abstract class will have some methods that are declared as abstract and some that are not. If you find yourself declaring a class that is entirely abstract, you are probably declaring what is known in Java as an interface. An interface is an entirely abstract class. You can derive subclasses from an interface in a manner completely analogous to deriving a subclass from another class.

As an example, suppose you are building an application that must display the hour of the day. Users will have two options for getting this information. They can get it from either a watch or a cuckoo clock. This could be implemented as follows:

interface Clock {
    public String GetTime(int hour);
}

class Cuckoo implements Clock  {
    public String GetTime(int hour) {
        StringBuffer str = new StringBuffer();
        for (int i=0; i < hour; i++)
            str.append("Cuckoo ");
        return str.toString();
    }
}

class Watch implements Clock  {
    public String GetTime(int hour) {
        return new String("It is " + hour + ":00");
    }
}

In this example, Clock is an interface that provides a single function, GetTime. What this means is that any class that is derived from (or, in other words, implements the Clock interface) must provide a GetTime function. Cuckoo is an example of a class that implements Clock, and you'll notice that instead of the class Cuckoo extends Clock syntax that would have been used if Clock were an abstract class, it is instead declared with class Cuckoo implements Clock.

Because Cuckoo implements the Clock interface, it provides a GetTime function. In this case, a string is created that will hold as many Cuckoos as specified by the hour parameter. The class Watch also implements Clock and provides a GetTime function. Its version is a simple message stating the hour.

Interfaces and superclasses are not mutually exclusive. A new class can be derived from a superclass and one or more interfaces. This could be done as follows for a class that implements two interfaces and has one superclass:

class MySubClass extends MySuperClass implements FirstInterface,
        SecondInterface {
    // class implementation
}

Because it is possible for one class to implement more than one interface, interfaces are a very convenient method for implementing a form of multiple inheritance.

Summary

This chapter covered a great deal of information. You were introduced to Java's primitive types and the operators that are available for these types. Next, you learned how to control the flow of a Java program through selection statements (if, switch, and case), iteration statements (for, while, and do…while), and jumping (break and continue). Finally, you learned how to put all of this together and create new classes by deriving them from existing classes or interfaces.