This chapter will quickly get you up to speed writing Java programs. If you have previously programmed in C++, this chapter will be a breeze. If you have programmed in some other language, the examples presented here will be familiar; you will just be learning a new syntax. If you have never programmed before, you will face the task of debugging your first programs. It will be easy or difficult depending on the mistakes you make and your ability to find programming errors. You may want to ask for help from someone who has programmed before.
In order to carry out the examples in this chapter and in the rest of the book, you need access to a computer that supports Java 1.0 or later. The type of computer and operating system that you use to write your programs won't matter. After all, that's the beauty of Java. The examples in this book have been developed using Java running under Windows 95. If you use Windows 95, I strongly recommend that you use a text editor other than Notepad or WordPad. These editors do not allow you to save files easily with the .java extension and will drive you crazy during program development. I use the shareware program TextPad, from Helios Software Solutions. It works well with both Windows 95 and NT, and is both convenient and affordable. It can be found in most Windows 95 FTP archives. If you are using Solaris, Linux, Windows NT, or any other Java port, you will use a text editor that is native to your system.
Since Brian Kernighan and Dennis Ritchie released the C programming language in 1978, the traditional first program has been to display Hello World! on the console display. Who are we to break with tradition?
Fire up your computer and change to the directory where you have Java installed. On my computer, Java is located in the c:\java directory. Create a subdirectory called jdg under your Java directory (that is, c:\java\jdg). If you are using a system such as UNIX or Windows NT, where you may not have write access to the java directory, create a jdg directory under your home directory.
You will store all the files that you develop under the jdg directory. Separate subdirectories will be created for each chapter, as shown in Figure 4.1. Go ahead and create a directory ch04 for this lesson.
Figure 4.1 : Files contained on the CD-ROM mirror the ones you'll create.
The CD-ROM that accompanies this book has an analogous directory structure to the one that you'll create. It contains the source and bytecode files for each example in the book. If you don't want to type in any of the sample programs, you can simply copy them from the CD-ROM. The CD-ROM also contains images, audio and video files, and other files used in the examples.
I recommend that you type in the first few programs. By doing so you will quickly get a feel for the Java syntax.
Now start your favorite text editor and key in the Java program in Listing 4.1.
Listing 4.1. The source code of the Hello World! program.package jdg.ch04;
/* HelloWorldApp.java */
import java.lang.System;
class HelloWorldApp {
/**
* Traditional "Hello World!" program.
*/
public static void main (String args[]) {
// Write to stdout.
System.out.println("Hello World!");
}
}
Save this program as HelloWorldApp.java in the c:\java\jdg\ch04 directory.
While in the c:\java\jdg\ch04 directory, enter javac HelloWorldApp.java. This invokes the Java compiler and compiles the HelloWorldApp.java source file, creating the HelloWorldApp.class binary file. A listing of your directory should look similar to the following:
C:\java\jdg\ch04>dir
Volume in drive C is ATHOME
Volume Serial Number is 1CE3-2551
Directory of C:\java\jdg\ch04
. <DIR> 01-24-96 10:42p .
.. <DIR> 01-24-96 10:42p ..
HELLOW~1 JAV 265 01-22-96 3:38p HelloWorldApp.java
HELLOW~1 CLA 487 01-24-96 10:45p HelloWorldApp.class
2 file(s) 752 bytes
2 dir(s) 348,585,984 bytes free
If you receive any compiler errors, go back to your editor and make sure that you typed the program correctly. Then recompile your program using javac. Make sure you set your PATH and CLASSPATH environment variables as discussed in Chapter 3, "Using the Java Developer's Kit." PATH tells your operating system shell where to find your Java Developer's Kit programs. CLASSPATH tells the Java runtime system where to find Java classes.
When you're ready to run HelloWorldApp, using the Java interpreter, enter java jdg.ch04.HelloWorldApp from your shell command prompt. You should get the following output:
C:\java\jdg\ch04>java jdg.ch04.HelloWorldApp
Hello World!
Note |
If you get an error message saying that Java can't find the HelloWorldApp class, make sure that your CLASSPATH is correctly set. |
At this point, you are probably not impressed with the power of Java-but you soon will be. Let's walk through the program source code and learn some Java syntax.
Java allows three kinds of comments. You can use any of these comment styles to document your Java programs. An example of each kind is provided in the HelloWorldApp.java source code. The C-style comments begin with /* and end with */. Here is an example:
/* HelloWorldApp.java */The Java automated documentation support comments begin with /** and end with */. They are found immediately before or after a Java declaration:
The C++-style comments begin with // and continue to the end of a line:
// Write to stdout.
/**
* Traditional "Hello World!" program.
*/
See Chapter 10, "Automating Software Documentation," for more information about this Java feature.
Java programs are built from classes and interfaces. A class defines a set of data structures, called variables, and the operations, referred to as methods, that are permitted on the variables. An interface defines a collection of methods that are to be implemented by a class. Your HelloWorldApp program was built from the HelloWorldApp class. It also uses the System class. Figure 4.2 summarizes the Java program structure.
Figure 4.2 : The Java program structure.
Classes and interfaces are organized into .java files that are separately compiled. These .java files are called compilation units. The HelloWorldApp.java file that you created with your text editor and compiled using javac is an example of a compilation unit.
The classes and interfaces contained in compilation units are organized into packages. Packages are used to group related classes and interfaces and to avoid naming conflicts. Your HelloWorldApp class is in the jdg.ch04 package. The System class, referenced by your program, is in the java.lang package.
The package statement identifies which package a compilation unit is in. The package statement must be the first statement in a compilation unit. Its syntax is
package packageName;
For example, the package statement
package jdg.ch04;
was used to identify the package containing the HelloWorldApp as jdg.ch04.
If a compilation unit does not contain a package statement, the classes and interfaces contained in the compilation unit are put into a default package-the package with no name. This default package is the same for all classes within a particular directory.
The java.lang.System class is used to display Hello World!. The System class is in the java.lang package. In order to tell the compiler to use the System class in the java.lang package (as opposed to the System class in another package), import the System class using the import statement. Importing a class tells the compiler to use that class when it compiles your source code file.
The syntax of the import statement is
import fullClassName;
The class name supplied with the import statement must be a fully qualified class name, as described in Chapter 3. This means that the name of the package containing the class must be prepended to the name of the class.
For example, the following import statement imports the System class from java.lang:
import java.lang.System;
The * wildcard character can be used instead of the class name in the import statement. It indicates that all classes in the package should be imported. For example, the following import statement imports all classes in the java.lang package:
import java.lang.*;
An alternative to using the import statement is to prefix the name of a class with its package name. For example, the statement
java.lang.System.out.println("Hello World!");
could have been used in place of
import java.lang.System;
.
.
.
System.out.println("Hello World!");
The last statement above would be replaced by the sample prefix statement.
It is generally easier to use the import statement than to spell out package names. In the case where two or more classes of the same name are imported into a compilation unit, you must prepend the package name when referencing one of the ambiguous classes.
The HelloWorldApp program was built on the HelloWorldApp class. This class is declared beginning with
class HelloWorldApp {
The class declaration ends with the last brace (}) of the file. HelloWorldApp declares and implements one method-the main method:
public static void main (String args[]) {
// Write to stdout.
System.out.println("Hello World!");
}
The main method is the method that is executed when a class is run from the command line using the Java interpreter. For example, the statement
java jdg.ch04.HelloWorldApp
causes the main method of the jdg.ch04.HelloWorldApp class to be executed.
The main method is always defined as public (that is, publicly accessible), static (applying to the class as a whole), and in the case of HelloWorldApp, void (no return value). The args[] parameter of main is defined as an array of class String. The args[] parameter is used to pass command-line arguments to the main method. Don't worry if you don't understand these terms-they will all be defined by the end of Chapter 5, "Classes and Objects."
The implementation of the main method consists of the following statement:
System.out.println("Hello World!");
This statement executes the println method of the object referred to by the out variable of the System class. The println method is executed using the "Hello World!" parameter. This is what causes Hello World! to be displayed on your console window.
The System class provides an interface to a number of useful system resources. Among these are the System.in and System.out input and output streams. The System.out stream was used in the preceding example. The following example illustrates the use of System.in.
This program builds on what you learned from HelloWorldApp. HelloWorldApp just displayed a message to your console window. The ICanReadApp program will read your name from the keyboard characters you type and display it on the console window. It introduces the concepts of identifiers, variable declarations, Java keywords, and object constructors.
Use your text editor to create a file called ICanReadApp.java with the Java program in Listing 4.2.
Listing 4.2. The source code of the I Can Read! program.// ICanReadApp.java
import java.lang.System;
import java.io.DataInputStream;
import java.io.IOException;
class ICanReadApp {
public static void main (String args[]) throws IOException {
System.out.print("Enter your name: ");
System.out.flush();
String name;
DataInputStream keyboardInput = new DataInputStream(System.in);
name=keyboardInput.readLine();
System.out.println("Your name is: "+name);
}
}
javac ICanReadApp.java
This will produce a file named ICanReadApp.class that contains the binary compiled code for your program. Run the program with the command line
java ICanReadApp
Make sure that your CLASSPATH is correctly set so that Java can find the ICanReadApp class.
The program will prompt you to enter your name. When you enter your name, the program will display it to you. Here is a sample program run:
C:\java\jdg\ch04>java ICanReadApp
Enter your name: Jamie
Your name is: Jamie
It may seem that you're going nowhere fast, but this little program illustrates some more basic Java syntax. Hang in there-by the time you get to the end of the chapter, you'll be having fun with Java console programming.
One of the first things you probably noticed about this program is that it doesn't contain a package statement. That was done deliberately to show you what happens when a package statement is omitted. The package name of the ICanReadApp class is set to the no name (blank) package, by default. This means that you don't have to prepend the package name to the class name in order to execute it using the interpreter. Although not using a package name might seem like a benefit, it also limits the extent to which the classes you develop can be accessed by other Java programs. Because the package name is blank and your CLASSPATH variable is .;c:\java, the ICanReadApp class can only be accessed from within the c:\java\jdg\ch04 directory. However, you can change your CLASSPATH to include this directory, as discussed in Chapter 2, "Java Overview."
The first line of the ICanReadApp program is a comment that identifies the name of the source file. Three import statements are used to import the java.lang.System, java.io.DataInputStream, and java.io.IOException classes into the compilation unit:
import java.lang.System;
import java.io.DataInputStream;
import java.io.IOException;
The ICanReadApp class is then declared. It consists of a single method called main. The main method contains a throws clause that identifies the fact that an IOException may be thrown during the course of its execution. When an exception is thrown, program control immediately transfers to an exception handler. This issue is covered in Chapter 7, "Exceptions."
The main method consists of the following six statements. These statements are summarized and then explained in the following subsections:
System.out.print("Enter your name: ");
System.out.flush();
String name;
DataInputStream keyboardInput = new DataInputStream(System.in);
name=keyboardInput.readLine();
System.out.println("Your name is: "+name);
The first statement displays the prompt Enter your name: on the console window.
The second statement flushes the output to the console to make sure that the data is displayed, even though a line termination was not sent to the console.
The third statement declares a variable called name of class String.
The fourth statement declares a variable named keyboardInput of class DataInputStream. It then creates an object of class DataInputStream that is constructed from the System.in object. This new object is then assigned to keyboardInput.
The fifth statement reads a line of data from the keyboardInput object and assigns it to the name variable.
The last statement displays the string Your name is: followed by the value of the object referred to by the name variable.
Statements three and four of the main method declare two variables: name and keyboardInput. Variables are used to refer to data of a predefined Java type, an array of values, an object of a particular Java class, or an object that implements a particular Java interface. Variables are given names, called identifiers. The type of data that the variable refers to is specified in the variable declaration. The name variable is declared to refer to an object of class String. The keyboardInput variable is declared to refer to an object of class DataInputStream.
Notice the difference between statements three and four. In statement three, the name variable is declared-nothing more. No objects are created and referred to by name. It is like a blank street sign. We know it is a street sign, but we don't know on what street it will be posted.
In statement four, after keyboardInput is declared, it is assigned a new object of class DataInputStream that is created using the new operator and the System.in parameter. The new operator is used to create an object that is an instance of a particular class. You'll learn all about classes in Chapter 5. The keyboardInput variable refers to the object that is created.
The name variable is assigned an object in line five. When the readLine method is applied to the object referred to by keyboardInput, an object of class String is created. This object is created and initialized with the keyboard data that you type in response to the Enter your name: prompt. The assignment statement causes name to refer to this newly created String object.
Identifiers are used to name variables, classes, interfaces, methods, and other Java language elements. An identifier is a sequence of characters that starts with an underscore (_), a dollar sign ($), or a letter (ASCII or Unicode). Subsequent characters may contain these characters plus the digits 0 through 9. Unicode letters come from the Unicode character set and are covered in Chapter 11, "Language Summary." Java reserves certain identifiers as keywords. Their use is restricted by Java and cannot be used in any other way. The reserved Java keywords are also listed in Chapter 11.
The following are valid identifiers:
myID
_score
$money
$$99__
These are not valid identifiers:
2time
dog#
spaced out
The problem with 2time is that it begins with a digit. dog# contains a pound (#) character that is not allowed in identifiers. The last example fails because it contains a space character.
Console (that is, nonWindows) programs process user keyboard input and display data to the console window. The console window is an MS-DOS window in Windows 95 and NT implementations of Java and a shell, or xterm window, in UNIX-based Java implementations. In the HelloWorldApp program, you learned how to write to the console window. The ICanReadApp program showed how to read from the keyboard.
You might compare System.in with System.out
and wonder why I had to create an object of class DataInputStream.
The System.out variable refers to an object of class
PrintStream. The PrintStream class provides
the println method for writing to objects of this class.
The System.in variable refers to an object of the InputStream
class. The methods provided by the InputStream class
aren't all that great for reading a line of text entered at the
keyboard and returning that data as a string. The InputStream
methods are fairly primitive. The DataInputStream class
is a subclass of FilterInputStream, which is a subclass
of InputStream. A subclass is a class that is built on
another class as a foundation. The methods of DataInputStream
build on the methods of FilterInputStream and InputStream.
The readLine method is one such method. The example uses
the DataInputStream class because it provides an easier
method of reading keyboard input.
Note |
Don't worry about learning all the new classes mentioned in this chapter. They are all covered in Part III, "Using the Java API." |
In statement four, when the new DataInputStream object is created, it uses the object referred to by System.in as a foundation.
In the ICanReadApp program, you were introduced to variable declarations and the construction and assignment of objects to variables. Variables may refer to objects of a particular class, to objects of one of the predefined Java types, to arrays, or to objects that implement a particular interface. You have already encountered the first case. The TypeThisApp program introduces the primitive Java types. Arrays are presented in the last example of this chapter. Interfaces are covered in Chapter 6, "Interfaces."
Start up your text editor and enter the program code shown in Listing 4.3. Then save it as TypeThisApp.java in your ch04 directory.
Listing 4.3. The Type This! program.// TypeThisApp.java
import java.lang.System;
class TypeThisApp {
public static void main (String args[]) {
// Integer types
byte oneByte = 57;
short twoBytes = 1024;
int fourBytes = 1234567;
long eightBytes = 0x123456789ABCDEFl;
// Floating-point types
float thirtyTwoBits = 1234.56f;
double sixtyFourBits = 6.282E123;
// Boolean type
boolean ownSelf = true;
// Character type
char malcolm = 'X';
System.out.println(oneByte);
System.out.println(twoBytes);
System.out.println(fourBytes);
System.out.println(eightBytes);
System.out.println(thirtyTwoBits);
System.out.println(sixtyFourBits);
System.out.println(ownSelf);
System.out.println(malcolm);
}
}
Compile the program using the following command line:
javac TypeThisApp.java
This will produce the TypeThisApp.class file that you can execute using
java TypeThisApp
The following output should be displayed on your console window:
C:\java\jdg\ch04>java TypeThisApp
57
1024
1234567
81985529216486895
1234.56
6.282e+123
true
X
TypeThisApp, like HelloWorldApp and ICanReadApp, declares only one class with a single method-main. The main method consists of eight variable declarations and assignments followed by eight invocations of the println method for the System.out object.
The eight variable declarations declare variables of the primitive data types byte, short, int, long, float, double, boolean, and char. Each of the declarations is combined with an assignment of a literal value to the declared variable.
Java supports four major primitive data types: integer, floating point, boolean, and character. The integer type has four subtypes: byte, short, int, and long. These correspond to 1-byte, 2-byte, 4-byte, and 8-byte integer values. The floating point type consists of a 4-byte float subtype and an 8-byte double subtype. The floating point type follows IEEE 754, a recognized standard for floating-point arithmetic developed by the Institute of Electrical and Electronics Engineers.
The boolean type consists of the literal values true and false. boolean types are not automatically converted into integer types because they are not defined in terms of integers as they are in C and C++. Explicit conversion is required.
The character type uses the standard Unicode character set and is a 16-bit unsigned integer. Variables of the char type store single characters. The java.lang.String class is used to store strings of characters.
TypeThisApp illustrates the use of literal values with the primitive types. Integer literals can be expressed as decimal, hexadecimal, or octal values, using the conventions established by C and C++. An integer literal that begins with a 0 is assumed to represent an octal value. An integer literal beginning with 0x or 0X is assumed to be a hexadecimal value. An l or L appended to an integer literal indicates that the literal is of type long.
Floating-point literals use the standard exponential notation described in Chapter 11. Floating-point literals are of type double, by default. An f or F appended to a floating-point literal indicates that the literal is of type float.
Boolean types simply use the values true and false.
Character types use standard Unicode, which is a superset of ASCII. Unicode is covered in Chapter 11. The C and C++ conventions for representing character literals are used by Java.
The programs you've written so far in this chapter have been deliberately kept short and simple. Their purpose is to quickly get you started in Java programming and to cover some of the basic elements of the Java language. The next example allows you to spread your wings and have a little fun at the same time. The BlackJackApp program that you will develop in this section is a simplified, character-based version of the popular blackjack card game. This example, while entertaining, illustrates the use of Java arrays and provides many examples of Java statements and expressions.
The BlackJackApp program is rather long compared to the previous examples. You have the option of copying the source code from the CD-ROM or typing it in yourself. I recommend typing it in. By doing so you will be sure to cover every statement in the program and increase your knowledge of Java syntax. Depending on how accurately you type, you might be called upon to develop some Java debugging skills.
Listing 4.4 is the program source code. Either type it into a file and save it as c:\java\jdg\ch04\BlackJackApp.java, or copy the file \java\jdg\ch04\BlackJackApp.java from the CD-ROM drive to your ch04 directory.
Listing 4.4. The source code of the BlackJack program.// BlackJackApp.java
// Import all the Java API classes needed by this program.
import java.lang.System;
import java.lang.Integer;
import java.lang.NumberFormatException;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.Random;
class BlackJackApp {
public static void main (String args[]) throws IOException {
// Create a BlackJackGame object ...
BlackJackGame game = new BlackJackGame();
// and play it!
game.play();
}
}
class BlackJackGame {
// Variable declarations
int bet;
int money;
Deck deck;
Hand playersHand;
Hand dealersHand;
DataInputStream keyboardInput;
// Method declarations
public BlackJackGame() { // Constructor
bet = 0;
money = 1000;
deck = new Deck();
keyboardInput = new DataInputStream(System.in);
}
void play() throws IOException {
System.out.println("Welcome to Blackjack!");
System.out.println("You have $"+Integer.toString(money)+".");
do {
placeBet();
if(bet>0) {
initialDeal();
if(playersHand.blackjack()) playerWins();
else{
while(playersHand.under(22) && playerTakesAHit()) {
playersHand.addCard(deck.deal());
playersHand.show(false,false);
}
while(dealersHand.mustHit())
dealersHand.addCard(deck.deal());
dealersHand.show(true,false);
showResults();
}
}
} while (bet>0);
}
void placeBet() throws IOException, NumberFormatException {
do{
System.out.print("Enter bet: ");
System.out.flush();
bet = Integer.parseInt(keyboardInput.readLine());
} while(bet<0 || bet>money);
}
void initialDeal() {
System.out.println("New hand...");
playersHand = new Hand();
dealersHand = new Hand();
for(int i = 0;i<2;++i) {
playersHand.addCard(deck.deal());
dealersHand.addCard(deck.deal());
}
dealersHand.show(true,true);
playersHand.show(false,false);
}
void playerWins() {
money += bet;
System.out.println("Player wins $"+Integer.toString(bet)+".");
System.out.println("Player has $"+Integer.toString(money)+".");
}
void dealerWins() {
money -= bet;
System.out.println("Player loses $"+Integer.toString(bet)+".");
System.out.println("Player has $"+Integer.toString(money)+".");
}
void tie() {
System.out.println("Tie.");
System.out.println("Player has $"+Integer.toString(money)+".");
}
boolean playerTakesAHit() throws IOException {
char ch = ' ';
do{
System.out.print("Hit or Stay: ");
System.out.flush();
String playersDecision = keyboardInput.readLine();
try ch = playersDecision.charAt(0);
catch (StringIndexOutOfBoundsException exception) ;
if(ch == 'H' || ch == 'h') return true;
if(ch == 'S' || ch == 's') return false;
} while(true);
}
void showResults() {
if(playersHand.busted() && dealersHand.busted()) tie();
else if(playersHand.busted()) dealerWins();
else if(dealersHand.busted()) playerWins();
else if(playersHand.bestScore() > dealersHand.bestScore()) playerWins();
else if(playersHand.bestScore() < dealersHand.bestScore()) dealerWins();
else tie();
}
} // End of BlackJackGame class
class Deck {
// Variable declarations
int cards[]; // Array of 52 cards
int topCard; // 0-51 (index of card in deck)
Random random;
// Method declarations
public Deck() { // Constructor
cards = new int[52];
for(int i = 0;i<52;++i) cards[i] = i;
topCard = 0;
random = new Random();
shuffle();
}
public void shuffle() {
// Repeat 52 times
for(int i = 0;i<52;++i) {
// Randomly exchange two cards in the deck.
int j = randomCard();
int k = randomCard();
int temp = cards[j];
cards[j] = cards[k];
cards[k] = temp;
}
}
int randomCard() {
int r = random.nextInt();
if(r<0) r = 0-r;
return r%52;
}
Card deal() {
if(topCard>51) {
shuffle();
topCard = 0;
}
Card card = new Card(cards[topCard]);
++topCard;
return card;
}
} // End of Deck class
class Hand {
// Variable declarations
int numCards;
Card cards[];
static int MaxCards = 12;
//Method declarations
public Hand() { // Constructor
numCards = 0;
cards = new Card[MaxCards];
}
void addCard(Card c) {
cards[numCards] = c;
++numCards;
}
void show(boolean isDealer,boolean hideFirstCard) {
if(isDealer) System.out.println("Dealer:");
else System.out.println("Player:");
for(int i = 0;i<numCards;++i) {
if(i == 0 && hideFirstCard) System.out.println(" Hidden");
else System.out.println(" "+cards[i].value+" of "+cards[i].suit);
}
}
boolean blackjack() {
if(numCards == 2) {
if(cards[0].iValue == 1 && cards[1].iValue == 10) return true;
if(cards[1].iValue == 1 && cards[0].iValue == 10) return true;
}
return false;
}
boolean under(int n) {
int points = 0;
for(int i = 0;i<numCards;++i) points += cards[i].iValue;
if(points<n) return true;
else return false;
}
int bestScore() {
int points = 0;
boolean haveAce = false;
for(int i = 0;i<numCards;++i) {
points += cards[i].iValue;
if(cards[i].iValue == 1) haveAce = true;
}
if(haveAce) {
if(points+10 < 22) points += 10;
}
return points;
}
boolean mustHit() {
if(bestScore()<17) return true;
else return false;
}
boolean busted() {
if(!under(22)) return true;
else return false;
}
} // End of Hand class
class Card {
// Variable declarations
int iValue; // Numeric value corresponding to card.
String value; // "A" "2" through "9" "T" "J" "Q" "K"
String suit; // "S" "H" "C" "D"
// Method declarations
public Card(int n) { // Constructor
int iSuit = n/13;
iValue = n%13+1;
switch(iSuit) {
case 0:
suit = "Spades";
break;
case 1:
suit = "Hearts";
break;
case 2:
suit = "Clubs";
break;
default:
suit = "Diamonds";
}
if(iValue == 1) value = "Ace";
else if(iValue == 10) value = "Ten";
else if(iValue == 11) value = "Jack";
else if(iValue == 12) value = "Queen";
else if(iValue == 13) value = "King";
else value = Integer.toString(iValue);
if(iValue>10) iValue = 10;
}
int getValue() {
return iValue;
}
} // End of Card class
Having produced BlackJackApp.java, in one way or another, compile it using the command line
javac BlackJackApp.java
This will produce the BlackJackApp.class file. If your file does not compile, go back and fix any typing errors and try again. Once you have a successful compile, execute the program using
java BlackJackApp
This will result in the following display:
C:\java\jdg\ch04>java BlackJackApp
Welcome to Blackjack!
You have $1000.
Enter bet:
The BlackJackApp program will provide you with $1000 with which to play blackjack. You use this money to bet. You place a bet between 0 and the amount of money you have, and then the computer, acting as dealer, will deal two cards to you and two to itself. For example, upon entering a bet of 10, I received the following program output:
C:\java\jdg\ch04>java BlackJackApp
Welcome to Blackjack!
You have $1000.
Enter bet: 10
New hand...
Dealer:
Hidden
2 of Hearts
Player:
Queen of Clubs
3 of Spades
Hit or Stay:
I, being the player, was dealt a queen of clubs and a three of
spades. This gives me a total of 13 points. Points are calculated
as follows:
Card Point | Value |
Ace | 1 or 11 (whichever is better) |
2 through 10 | face value of card (that is, 2 through 10) |
Jack, Queen, King | 10 |
The objective of the game is to get as close to 21 as you can, without going over. Whoever gets the closest wins. If you go over 21, you lose, unless the dealer does also, in which case you tie.
When you are dealt your initial two cards, you are shown one of
the dealer's cards. This helps you to determine whether you should
take another card, referred to as hitting, or stay
with the cards that you have. You can enter h or s
to inform the dealer of your decision. If you enter h,
you will be dealt another card. If you enter s, the dealer
will begin to play its hand.
Note |
If the point total of your first two cards is 21, you are said to have blackjack and immediately win. |
The dealer must take a hit until the total points in its hand is 17 or over, at which point it must stay. When both you and the dealer have finished playing your hands, the total number of points acquired by each is used to determine the winner. Play is repeated until you enter a 0 bet.
The following program output shows a game played between myself and the BlackJackApp program:
C:\java\jdg\ch04>java BlackJackApp
Welcome to Blackjack!
You have $1000.
Enter bet: 10
New hand...
Dealer:
Hidden
2 of Hearts
Player:
Queen of Clubs
3 of Spades
Hit or Stay: h
Player:
Queen of Clubs
3 of Spades
7 of Spades
Hit or Stay: s
Dealer:
Queen of Spades
2 of Hearts
5 of Spades
Player wins $10.
Player has $1010.
Enter bet: 20
New hand...
Dealer:
Hidden
7 of Clubs
Player:
King of Clubs
9 of Spades
Hit or Stay: s
Dealer:
2 of Clubs
7 of Clubs
9 of Clubs
Player wins $20.
Player has $1030.
Enter bet: 0
C:\java\jdg\ch04>
On the initial deal, I bet 10 bucks. I was given a queen of clubs and a three of spades, for a total of 13 points. The dealer was given a two of hearts and another (hidden) card. I elected to take a hit and was dealt a seven of spades, bringing the total in my hand up to 20 points-beginner's luck! The dealer turned over the hidden card to reveal a queen of spades. He then drew a five of spades for a total of 17 points. Because the dealer reached 17, he was forced to stay, and I had won $10. Feeling a little lightheaded, I proceeded to double my bet to $20. I was dealt a king of clubs and a nine of spades for a total of 19 points. I decided to stay with that hand. The dealer's hand was revealed to be a two of clubs and a seven of clubs. The dealer drew a nine of clubs for a total of 18 points. I had won again! At that point I elected to take the money and continue writing this book. I entered a 0 bet to end the game.
The point of the example is not to turn you into a blackjack gambler, but to serve as a more interesting example from which to discuss Java arrays, statements, and expressions.
The BlackJackApp.java file is long, but don't let that daunt you. I'm going to break it down, class by class, and method by method, to explain its operation.
The program begins with a comment identifying the name of the program:
// BlackJackApp.java
It then imports all the Java API classes it needs to perform its processing:
// Import all the Java API classes needed by this program.
import java.lang.System;
import java.lang.Integer;
import java.lang.NumberFormatException;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.Random;
Next, it declares the BlackJackApp class, the class that implements your blackjack application. This class has a single main method, like all the other programs you've developed so far. The main method consists of two Java statements. The first declares the game variable as having class type BlackJackGame and assigns it a new object of class BlackJackGame. The second statement applies the play() method to the object referenced by game, as shown in the following code:
class BlackJackApp {
public static void main (String args[]) throws IOException {
// Create a BlackJackGame object ...
BlackJackGame game = new BlackJackGame();
// and play it!
game.play();
}
}
The BlackJackGame class is not defined as part of the Java API. I wonder why they left it out? Because it doesn't exist anywhere else, it is a class that must be declared as part of the program. The BlackJackGame class and other classes could have been defined and compiled, separately, but they were combined into a single compilation unit to keep this example somewhat compact.
The BlackJackGame class is rather long. It declares six variables and nine methods. The variables are data structures that represent the state of a blackjack game. The bet variable identifies the amount wagered by the player. The money variable identifies how much money the player has left. The deck variable references an object of class Deck that is used to represent a deck of cards. Two Hand variables are declared, representing the player's hand and the dealer's hand. Finally, our old friend keyboardInput has returned for a repeat performance:
class BlackJackGame {
// Variable declarations
int bet;
int money;
Deck deck;
Hand playersHand;
Hand dealersHand;
DataInputStream keyboardInput;
.
.
.
}
The first method declared for BlackJackGame is its constructor. A constructor is used to initialize objects that are new instances of a class. In the main method of the BlackJackApp class, the BlackJackGame() constructor is invoked to initialize the newly created BlackJackGame object that is assigned to game:
BlackJackGame game = new BlackJackGame();
The BlackJackGame() constructor initializes four of the six variables of the BlackJackGame class. The player's bet is set to 0, and the player is given $1000. The playersHand and dealersHand variables are not initialized until the cards are dealt.
A new Deck object is created and assigned to the deck variable. The new object is initialized using the Deck() constructor for the Deck class. If you typed in the program, you probably know where to find it in the source code listing.
Finally, the keyboardInput variable is assigned a new object of class DataInputStream. This object is created using the DataInputStream() constructor with the System.in variable as an argument:
// Method declarations public BlackJackGame() { // Constructor
bet = 0;
money = 1000;
deck = new Deck();
keyboardInput = new DataInputStream(System.in);
}
Note |
An argument is a value that is provided as an input to a method invocation. It does not denote disagreement. |
The second method defined for BlackJackGame is the play() method. This method is invoked in the main method of BlackJackApp to cause the BlackJackGame object, referenced by game, to be played:
game.play();
The play() method begins with the void keyword to indicate that it does not return any value. It also identifies the fact that IOException may be thrown during its processing. Exceptions are covered in Chapter 7. The general structure of the play() method is as follows:
void play() throws IOException {
.
.
.
}
The play() method begins by displaying the Welcome to Blackjack! text and the amount of money available to the player. The second println() method takes three arguments. First it displays You have $, then it displays the contents of the money variable, and then it displays a period (.). It converts the integer value of money to a String value before printing it. String is a class defined in the Java API to represent strings of characters. These statements are as follows:
System.out.println("Welcome to Blackjack!");
System.out.println("You have $"+Integer.toString(money)+".");
The rest of the statements of the play() method are surrounded by
do {
.
.
.
} while (bet>0);
This is a do statement, and it causes the statements between the braces to be repeatedly executed while the value of bet is greater than 0.
The block of statements within the do statement begins with an invocation of the placeBet() method. Because no object is identified with the placeBet() method, it is invoked using the current object-that which is invoked with the play() method:
placeBet();
The placeBet() method, as you'll see shortly, is used to prompt the player to enter his bet. After the placeBet() method is invoked, the next statement is an if statement that checks whether bet is greater than 0. If bet is greater than 0, the statements between its braces are executed.
If bet is not greater than 0, execution continues after the if statement. In this case, the end of the do statement is encountered, the do statement terminates, the play() procedure returns, and the BlackJackApp main method finishes its processing. In other words, the game is over.
The following code tests whether bet is greater than 0:
if(bet>0) {
.
.
.
}
If bet is greater than 0, the initialDeal() method is invoked. This method is used to deal a new hand to the player and to the dealer. It causes the playersHand and dealersHand variables to each be initialized with an object of class Hand. The initialDeal() method is invoked using the following code:
initialDeal();
Another if statement is then executed. This if statement checks to see if the player was dealt blackjack (21 points). It does this by invoking the blackjack() method for the object referenced by the playersHand variable. In the case that the blackjack() method returns the boolean value true, the player wins the bet, and the playerWins() method is invoked. If the player was not fortunate enough to be dealt a blackjack, the statements within the else part of the if statement are executed, as shown in the following code:
if(playersHand.blackjack()) playerWins();
else{
.
.
.
}
The else part begins with a while statement. A while statement is similar to a do statement in that it repeatedly executes the block of statements enclosed by braces. It differs from the do statement in that it checks to see if it is finished before executing the statement block. The while statement checks to see if the player has 21 or fewer points in his hand and whether he wants to take a another card. It does this by invoking the under() method for the object referenced by the playersHand variable, passing it the integer 22 as an argument. If the under() method returns the boolean value true, the playerTakesAHit() method is invoked to prompt the player to hit or stay. If the user elects to take a hit, playerTakesAHit() returns a boolean true, and the statements enclosed by the while statement are executed. If either under() or playerTakesAHit() returns false, the next statement after the while statement is executed.
The statements enclosed within the while statement invoke methods for the Hand object referenced by the playersHand variable. The first method causes a card to be added to the player's hand by dealing it from the deck. The second method determines if and how the player's hand should be displayed. The code that performs this processing follows:
while(playersHand.under(22) && playerTakesAHit()) {
playersHand.addCard(deck.deal());
playersHand.show(false,false);
}
The previous while statement is followed by another while statement. This while statement does not enclose a block of statements within braces. It only applies to a single statement:
while(dealersHand.mustHit())
dealersHand.addCard(deck.deal());
The while statement is used to play the dealer's hand. It invokes the mustHit() method with the object referenced by the dealersHand variable to determine whether the dealer has fewer than 17 points in his hand and, therefore, must take a hit. If the dealer must take a hit, the addCard() method is invoked to deal a card to the dealer.
After the dealer's hand is played, the show() method is invoked to display it to the console. The showResults() method is then invoked to show the results of the hand. This concludes the description of the play() method. It's a good idea to review the source code of the play() method to make sure that you know how it works before going on. The following statements invoke the show() and showResults() methods:
dealersHand.show(true,false);
showResults();
The placeBet() method is invoked by the play() method to prompt the player to enter a bet. It declares two potential exceptions in its throw clause.
The placeBet() method uses a do statement to repeatedly prompt the user to enter a bet that is at least 0 and at most is the amount of money that he has left. The statement block enclosed by the do statement displays the prompt, reads the line entered by the user, converts it to an integer, and then assigns it to the bet variable. The source code of the placeBet() method follows:
void placeBet() throws IOException, NumberFormatException {
do{
System.out.print("Enter bet: ");
System.out.flush();
bet = Integer.parseInt(keyboardInput.readLine());
} while(bet<0 || bet>money);
}
The initialDeal() method is invoked by the play() method to deal a new hand to the player and the dealer. It displays the New hand text to the console window to inform the player that a new hand is being dealt. It then creates two new objects of class Hand, initializes them with the Hand() constructor, and assigns them to the playersHand and dealersHand variables. The source code of the initialDeal() method follows:
void initialDeal() {
System.out.println("New hand...");
playersHand = new Hand();
dealersHand = new Hand();
.
.
.
}
After creating the two new hands, the initialDeal() method executes a for statement. The for statement iterates the execution of the block of statements enclosed by braces, based on the conditions identified immediately before the statement block. In this case a variable, i, of type int, is created for the duration of the for statement's execution and assigned a value of 0. The statement block is then executed while i is less than 2. Each time the statement block is executed, the value of i is incremented by 1. The expression ++i causes i to be incremented by 1.
The for statement is used to sequentially deal two cards to the player and two to the dealer by invoking the addCard() method. Note that the value returned by the deal() method is used as an argument to the addCard() method, in both instances. The source code of the for statement follows:
for(int i = 0;i<2;++i) {
playersHand.addCard(deck.deal());
dealersHand.addCard(deck.deal());
}
After the player and dealer have been dealt their hands, the mysterious show() method is invoked, as shown in the following code, to display the new hands (you'll find out what the boolean values are used for when you study the show() method):
dealersHand.show(true,true);
playersHand.show(false,false);
The next three methods, playerWins(), dealerWins(), and tie(), are used to update the money variable based on the bet variable and the outcome of the hand:
void playerWins() {
money += bet;
System.out.println("Player wins $"+Integer.toString(bet)+".");
System.out.println("Player has $"+Integer.toString(money)+".");
}
void dealerWins() {
money = bet;
System.out.println("Player loses $"+Integer.toString(bet)+".");
System.out.println("Player has $"+Integer.toString(money)+".");
}
void tie() {
System.out.println("Tie.");
System.out.println("Player has $"+Integer.toString(money)+".");
}
These methods also display the results to the player by converting the values of bet and money to String objects. The += operator causes the value of bet to be added to the value of money and assigned to the money variable. Similarly, the = operator causes the value of bet to be subtracted from the value of money before it is assigned to the money variable.
The playerTakesAHit() method is an example of a method that returns a result. The boolean keyword at the beginning of the method declaration specifies that the method should return a result of type boolean. Any valid primitive type, array type, class type, or interface type can be used to specify the return type of a method. For example, the return type could be long, String, or an array of double values.
The method begins by declaring a variable of type char and assigning it a space character. It then executes an infinite do statement. The statement is infinite because the while condition at the end of the do statement is literally always true. This doesn't mean that the statement will execute forever, though. Return statements within the block of the do statement will cause statement execution to return to the method that invoked playerTakesAHit().
The do block begins by displaying the Hit or Stay: prompt to the player and reads the player's input from the keyboard. A try statement is then executed. The try statement executes a statement or block of statements and, if an exception is thrown, uses a catch clause to process the exception. This try statement sets the variable ch to the first character of the playersDecision variable. The playersDecision variable references a String object that is created when the player's input is read from the keyboard. The charAt() method is defined in the String class of the Java API. If the player enters a blank line, the StringIndexOutOfBoundsException will be thrown. The catch clause is used to prevent the exception from terminating program execution.
If the character assigned to ch, via playersDecision, is H or h, the value of true is returned as the result of the playerTakesAHit() method. If ch equals S or s, false is returned. Otherwise, the do statement causes the player to be repeatedly prompted until he hits or stays. The playerTakesAHit() method follows:
boolean playerTakesAHit() throws IOException {
char ch = ' ';
do{
System.out.print("Hit or Stay: ");
System.out.flush();
String playersDecision = keyboardInput.readLine();
try ch = playersDecision.charAt(0);
catch (StringIndexOutOfBoundsException exception) ;
if(ch == 'H' || ch == 'h') return true;
if(ch == 'S' || ch == 's') return false;
} while(true);
}
The showResults() method is the last method declared for the BlackJackGame class. This method illustrates the use of nested if statements. The first if statement checks to see if the player's hand and the dealer's hand are both busted (over 21 points). If so, the tie() method is invoked to display the results to the player. If not, the statement following the else is executed. This turns out to be another if statement.
The second if statement checks to see if the player's hand is busted. Because the else part of the first if statement was executed, it is impossible for both the player and the dealer to be busted. So, if the player is busted, the dealer wins.
The third if statement is executed in the else parts of the first and second if statements. It uses the same logic as the second if statement to determine whether the dealer busted and the player wins.
The fourth if statement is only executed if neither the player nor the dealer busted. It checks the points in both of their hands to see if the player is higher than the dealer and, therefore, is the victor.
The fifth if statement is only executed if neither busts and the player is not higher than the dealer. If the dealer is higher than the player, the dealer wins. If the dealer is not higher than the player, the final else part is executed. At this point, neither has busted, but neither is higher than the other, so both must have the same number of points and a tie is declared. The showResults() method follows:
void showResults() {
if(playersHand.busted() && dealersHand.busted()) tie();
else if(playersHand.busted()) dealerWins();
else if(dealersHand.busted()) playerWins();
else if(playersHand.bestScore() > dealersHand.bestScore()) playerWins();
else if(playersHand.bestScore() < dealersHand.bestScore()) dealerWins();
else tie();
}
The third class declared within BlackJackApp.java is the Deck class. It is used to simulate a deck of cards.
The Deck class declares three variables and four methods. The cards[] variable is an example of an array. Arrays are objects that contain a number of variables of the same type. The variables contained in an array are referred to as the component variables of the array and are referenced using the integer indices 0, ,n-1, where n is the number of components contained within the array. The cards[] array is declared to contain components of type int. The brackets ([]) indicate the declaration of an array. The topCard variable is an integer that identifies the next card to be dealt from the deck. The random variable is used to generate random numbers. It references objects that are of class java.util.Random, a class defined within the Java API. The variable declarations of the Deck class follow:
class Deck {
// Variable declarations
int cards[]; // Array of 52 cards
int topCard; // 051 (index of card in deck)
Random random;
.
.
.
}
The constructor for the Deck class allocates an array of 52 integers and assigns it to cards[]. The cards[] array simulates the 52 cards found in a normal deck of playing cards.
A for statement is used to assign 0 to cards[0], 1 to cards[1], 2 to cards[2], and so on, until 51 is assigned to cards[51]. This creates a deck of cards in which all the cards are ordered by suit and by value. The integers 0 through 51 are logically mapped to playing cards, as follows:
0 through 12 are mapped to the ace of spades through the king of spades
13 through 25 are mapped to the ace of hearts through the king of hearts
26 through 38 are mapped to the ace of clubs through the king of clubs
39 through 51 are mapped to the ace of diamonds through the king of diamonds
The topCard of the deck is set to 0. It is used as an index into the cards[] array. The random variable is assigned a new object of class Random. Finally, the shuffle() method is invoked to shuffle the new deck of cards. The constructor of the Deck class follows:
// Method declarations
public Deck() { // Constructor
cards = new int[52];
for(int i = 0;i<52;++i) cards[i] = i;
topCard = 0;
random = new Random();
shuffle();
}
The shuffle() method shuffles the deck of cards by randomly switching two cards in the deck 52 times. It does this by invoking the randomCard() method to generate a random integer between 0 and 51. These random integers are used to randomly select components of cards and exchange their values. The shuffle() method follows:
public void shuffle() {
// Repeat 52 times
for(int i = 0;i<52;++i) {
// Randomly exchange two cards in the deck.
int j = randomCard();
int k = randomCard();
int temp = cards[j];
cards[j] = cards[k];
cards[k] = temp;
}
}
The randomCard() method returns an integer between 0 and 51 inclusive. It identifies the int return value in its method declaration. It begins by declaring a variable r and assigning it a random integer value generated by applying the nextInt() method to the random variable. The nextInt() method is defined in the java.util.Random class. If the value assigned to r is less than 0, it is changed in sign to a positive integer. The randomCard() method then returns an integer between 0 and 51 by returning the random integer modulus 52. The randomCard() method follows:
int randomCard() {
int r = random.nextInt();
if(r<0) r = 0r;
return r%52;
}
The deal() method is used to deal a card off the top of the deck. It does this by using the topCard variable as an index into the cards[] array. It starts at 0 and is incremented until it is greater than 51, indicating that all the cards in the deck have been dealt. In this case, the deck is reshuffled, and topCard is set to 0 once again. This creates the effect of another deck being used because the player and dealer are not required to throw back any cards that are currently in their hands before the deck is shuffled.
The Card class is used to translate the integer card values to String values that can be displayed on the console. A card is dealt by constructing a new instance of Card using the value of cards[] indexed by topCard as an argument. topCard is then incremented to move to the next card in the deck. Note that deal() returns the object of class Card that was created using the Card() constructor. The deal() method follows:
Card deal() {
if(topCard>51) {
shuffle();
topCard = 0;
}
Card card = new Card(cards[topCard]);
++topCard;
return card;
}
The Hand class is used to implement a hand of cards as played by both the player and the dealer. It declares three variables and eight methods.
The numCards variable identifies the number of cards contained in the hand. The cards[] array has the same name as the cards[] array declared in the Deck class, but it is logically and physically distinct. Because it is declared in a separate class, it is contained in objects that are instances of the Hand class and not of the Deck class. The MaxCards variable is declared to be static. This means that it is used with the class, as a whole, and not with individual objects that are instances of the class. You'll learn more about class and instance variables in Chapter 5. MaxCards is used to identify the number of components to be allocated within cards[]. The Hand class is structured as follows:
class Hand {
// Variable declarations
int numCards;
Card cards[];
static int MaxCards = 12;
.
.
.
}
The constructor for the Hand class sets numCards to 0, to indicate an empty hand, and then creates a MaxCards size array of Card objects and assigns it to cards. The constructor for the Hand class follows:
//Method declarations
public Hand() { // Constructor
numCards = 0;
cards = new Card[MaxCards];
}
Cards are added to a hand using the addCard() method. This method takes an object of class Card as an argument and adds it to the first available position within the cards[] array. It then increments numCards so that it will index the next available position within cards[]. The addCard() method follows:
void addCard(Card c) {
cards[numCards] = c;
++numCards;
}
The show() method displays either the dealer's or the player's hand. It takes two boolean arguments that specify whether the hand belongs to the dealer, and if so, whether the first card should be hidden when the hand is displayed. The isDealer parameter is used in the initial if statement to determine whether a dealer or a player heading should be displayed. A for statement is then used to iterate numCards times in order to display each card of the hand. The statement block enclosed by the for statement uses the hideFirstCard parameter to determine whether the first card should be hidden or displayed. The show() method follows:
void show(boolean isDealer,boolean hideFirstCard) {
if(isDealer) System.out.println("Dealer:");
else System.out.println("Player:");
for(int i = 0;i<numCards;++i) {
if(i == 0 && hideFirstCard) System.out.println(" Hidden");
else System.out.println(" "+cards[i].value+" of "+cards[i].suit);
}
}
The blackjack() method returns a boolean value indicating whether the hand is blackjack. It uses an if statement to make sure that there are only two cards in the hand. If there are not two cards, false is returned to indicate that the hand is not blackjack. If the number of cards is exactly two, it uses the iValue variable of the Card objects contained in the cards[] array to determine whether the current hand is blackjack. The iValue variable is discussed with the Card class. It identifies the number of points associated with a card. A card with iValue = 1 is an ace. Aces can be either 1 or 11 points. The blackjack() method follows:
boolean blackjack() {
if(numCards == 2) {
if(cards[0].iValue == 1 && cards[1].iValue == 10) return true;
if(cards[1].iValue == 1 && cards[0].iValue == 10) return true;
}
return false;
}
The under() method returns a boolean value indicating whether the number of points in a hand is less than the argument passed via the n parameter. It declares a points variable of type int and uses a for statement to sum the points for all cards in the hand. It then checks to see if the number of points in the hand is less than n and returns an appropriate value of true or false. The under() method follows:
boolean under(int n) {
int points = 0;
for(int i = 0;i<numCards;++i) points += cards[i].iValue;
if(points<n) return true;
else return false;
}
The bestScore() method returns an integer value identifying the best possible point score for the hand. It adjusts the value associated with aces to either 1 or 11, depending on whether it causes the hand to go over 21 points. It uses a variable, haveAce, of type boolean, to identify whether the hand contains an ace. It uses a for statement to calculate the minimum number of points in the hand and to determine whether any aces are present. If an ace is found, it determines whether it is better to use the 11- or 1-point value of the ace. The bestScore() method follows:
int bestScore() {
int points = 0;
boolean haveAce = false;
for(int i = 0;i<numCards;++i) {
points += cards[i].iValue;
if(cards[i].iValue == 1) haveAce = true;
}
if(haveAce) {
if(points+10 < 22) points += 10;
}
return points;
}
The mustHit() method is used to play out the dealer's hand. If the bestScore of the dealer's hand is lower than 17, the dealer must take a hit. If it is 17 or higher, the dealer must stay. The mustHit() method follows:
boolean mustHit() {
if(bestScore()<17) return true;
else return false;
}
The busted() method uses an if statement to determine whether the number of points in a hand is under 22. If it is not under, the hand is busted, and true is returned. Otherwise, false is returned. The busted() method follows:
boolean busted() {
if(!under(22)) return true;
else return false;
}
The Card class is used to translate the integer value of cards, maintained by objects of the Deck class, into objects of type String. It declares three variables and two methods.
The iValue variable is used to keep track of the number of points associated with a card. It is an abbreviation for "integer value" and is used to differentiate it from the value variable. The value variable references a text string that is used to describe the face value of a playing card. The suit variable is used to identify the suit of a playing card. The variables declared for the Card class are shown:
class Card {
// Variable declarations
int iValue; // Numeric value corresponding to card.
String value; // "A" "2" through "9" "T" "J" "Q" "K"
String suit; // "S" "H" "C" "D"
.
.
.
}
The Card() constructor is the heart of the Card class and is an example of a constructor that takes an argument. It expects a value of 0 through 51 of a card value from the Deck class. The Card class constructor follows:
// Method declarations
public Card(int n) { // Constructor
.
.
.
}
Card() first determines the suit of the card identified by the n parameter. It does this by dividing n by 13 and assigning the result to an integer variable named iSuit. It determines the point value of the card by calculating n modulus 13 and adding 1. It adjusts this value later in the method. This is shown in the following code:
int iSuit = n/13;
iValue = n%13+1;
Card() then uses a switch statement to assign the correct text string to the suit variable. The switch statement takes the iSuit variable and compares it to the values identified in each of the case labels. If a case label matches the value of iSuit, control of execution is passed to the statement after the case label. These statements consist of assignment statements that set suit to the correct text string. The default label is used if no other label matches iSuit. The break statement is used to "jump out" of the execution of the switch statement to the statement immediately following the switch statement. It is also used with other statements, such as the for, while, and do statements. The switch statement follows:
switch(iSuit) {
case 0:
suit = "Spades";
break;
case 1:
suit = "Hearts";
break;
case 2:
suit = "Clubs";
break;
default:
suit = "Diamonds";
}
The statements following the switch statement show how a switch statement can be coded using a series of nested if statements:
if(iValue == 1) value = "Ace";
else if(iValue == 10) value = "Ten";
else if(iValue == 11) value = "Jack";
else if(iValue == 12) value = "Queen";
else if(iValue == 13) value = "King";
else value = Integer.toString(iValue);
if(iValue>10) iValue = 10;
These statements are equivalent to the following switch statement:
value=Integer.toString(iValue);
switch(iValue) {
case 1:
value = "Ace";
break;
case 10:
value = "Ten";
break;
case 11:
value = "Jack";
iValue = 10;
break;
case 12:
value = "Queen";
iValue = 10;
break;
case 13:
value = "King";
iValue = 10;
break;
}
Finally, the getValue() method is used to return the value of iValue, the point value of the card. It is fairly simple, as far as methods go, but it shows how the values of an object's variables can be made available without having to provide access to the variable itself. The getValue() method follows:
int getValue() {
return iValue;
}
Arrays are objects that contain a number of variables of
the same type. These component variables are referenced using
the integer indices 0,
,n-1, where n is the
length of the array. The type of the array is identified by appending
[] to the type of its components. For example, int[]
identifies an array of type int, Object[] identifies
an array of type Object, and char[][] identifies
an array of an array of type char.
Note |
Java only supports single-dimensional arrays. Multidimensional-array capabilities can be achieved by using arrays of arrays. |
Arrays are declared by declaring a variable to be of an array type. For example, the following declares nums to be an array of type int:
int[] nums;
The declaration can also be written as follows:
int nums[];
You can place the brackets after either the type or the variable name.
When a variable of an array type is declared, the size of the array is not identified, and the array object is not allocated. To allocate storage for an array, you can use the new operator to create an array object of a specific size. For example, the following statement:
char ch[] = new char[24];
creates a char array of length 24, the individual component variables of which can be referenced by ch[0], ch[2], , ch[23]. The following statement creates an array of type Dice[] of length 6:
Dice[] d = new Dice[6];
Arrays can also be allocated by specifying their initial values. For example, the following allocates a String array of length 7 that contains abbreviations for the days of the week:
String days[] = {"sun", "mon", "tue", "wed", "thu", "fri", "sat"};
The length of an array can always be found by appending .length to the name of the array. For example, days.length returns the integer 7 as the length of days[].
The BlackJackApp example introduces a number of Java
statements. These statements implement the bodies of the various
methods used in the example. The following subsections describe
the types of statements that are used in the BlackJackApp
example. A complete description of Java statements is provided
in Chapter 11. When you read through the
following sections and learn about a particular statement, go
back through the BlackJackApp program and see how many
examples of the statement you can find. This will help you to
associate the statement's syntax with the different contexts in
which it can be used and elevate your understanding from the syntactic
to the semantic level.
Note |
Java statements, like C and C++ statements, are separated by semicolons. |
Java statements are organized into statement blocks. Blocks
begin with an opening brace ({) and end with a closing
brace (}). They are used to indicate a group of statements
and variable declarations that are to be considered as a single
syntactical unit. Blocks are used to define the scope of execution
of statements in which they are enclosed. For example, the body
of a method can be considered to be a single block. Blocks are
used in other statements, such as the if and do
statements, to identify groups of statements that are to be executed
as if they were a single statement.
Note |
Statement blocks can be considered to be syntactically equivalent to a single statement. |
An example of a statement block is taken from the BlackJackGame() constructor:
bet = 0;
money = 1000;
deck = new Deck();
keyboardInput = new DataInputStream(System.in);
}
The if statement is used to decide whether a particular statement should be executed. Its syntax is as follows:
if ( BooleanExpression ) Statement1
else Statement2
The else part of the statement is optional. If the boolean expression, referred to as the if condition, evaluates to true, Statement1 is executed; otherwise, Statement2 is executed. Program execution then continues with the next statement following the if statement. If the else part is omitted, execution proceeds immediately to the next statement when the if condition is false. Either Statement1 or Statement2 can be a statement block.
An example of an if statement is taken from the show() method of the Hand class:
if(isDealer) System.out.println("Dealer:");
else System.out.println("Player:");
If the value of isDealer is true, the text Dealer: is displayed; otherwise, the text Player: is displayed.
The switch statement is like a sequence of embedded if statements. It is used to transfer control to the first labeled statement within a block of statements that matches the value of the expression in the switch expression. The syntax of the switch statement is
switch ( SwitchExpression ) StatementBlock;
where statements within the statement block are labeled by preceding them with prefixes of the form
case ConstantExpression :
or
default :
The switch expression must evaluate to a value of type char, byte, short, or int. The same is true of the constant expressions in the case labels. The switch statement evaluates the switch expression and transfers program execution to the first labeled statement whose constant expression has the same value as the switch expression. If no case-labeled expression matches the switch expression, control is transferred to the first statement with a default label. Otherwise, control is transferred to the next statement following the switch statement.
An example of a switch statement is taken from the Card() constructor:
switch(iSuit) {
case 0:
suit = "Spades";
break;
case 1:
suit = "Hearts";
break;
case 2:
suit = "Clubs";
break;
default:
suit = "Diamonds";
}
The value of iSuit is compared to the values 0, 1, and 2 of the case labels. If it matches any of these values, program execution is transferred to the labeled statement. Otherwise, program execution is transferred to the statement labeled as default. The break statements are used to transfer control to the first statement following the switch statement, as you'll learn in the following section.
The break statement is used to terminate execution of a statement block and transfer control to the first statement following the statement in which the block is enclosed. The syntax of the break statement is
break;
or
break label;
where label is an optional label that can be attached to the statement enclosing the statement block. Refer to Chapter 11 for a discussion of the use of labels with the break statement.
The break statement is used with the case, do, while, and for statements to exit the enclosed statement block and transfer control to the first statement following the enclosing statement.
The sample switch statement, shown in the previous section, contains several break statements that cause program execution to be transferred to the first statement following the switch statement.
The for statement is used to iterate the execution of a statement or statement block. Its syntax is as follows:
for (InitializationClause ForExpression; IncrementClause) EnclosedStatement
InitializationClause consists of a statement that is executed once at the beginning of the for statement. The for expression is then checked. If it is false, the for statement ends, and program execution continues with the next statement following the for statement. If the for expression is true, the enclosed statement is executed. The enclosed statement can be a statement block.
When the execution of the enclosed statement is completed, the
statement contained in the increment clause is executed. The for
expression is then reevaluated to determine whether the enclosed
statement should be executed again. The enclosed statement, increment
statement, and evaluation of the for expression repeat
their execution until the for expression evaluates to
false, at which point execution of the for statement
is complete and program execution continues with the statement
following the for statement.
Note |
The increment clause does not end with a semicolon (;). |
A sample for statement is taken from the under() method of the Hand class:
for(int i = 0;i<numCards;++i) points += cards[i].iValue;
This statement begins by setting the variable i to 0. It then checks to see if i is less than numCards. If it is not, the for statement terminates. If i is less than numCards, the statement
points += cards[i].iValue;
is executed. This statement is used to add the iValue
variable of the ith card[] array element to
the points variable. When execution of this statement
is completed, the ++i statement is executed to increment
i by 1. The for expression, i<numCards,
is reevaluated, and the for statement's execution continues.
Note |
The operators used by Java are very similar to those of C and C++. |
The do statement is used to repeatedly execute a statement until a specified condition becomes false. The syntax of the do statement is
do EnclosedStatement while (BooleanExpression) ;
The do statement repeatedly executes the enclosed statement until the boolean expression becomes false. The enclosed statement will be executed at least once because the boolean expression is evaluated after its execution. The enclosed statement can be a statement block.
An example of a do statement is taken from the placeBet() method of the BlackJackGame class:
do{
System.out.print("Enter bet: ");
System.out.flush();
bet = Integer.parseInt(keyboardInput.readLine());
} while(bet<0 || bet>money);
The do statement executes the statement block until a bet between 0 and the value of money is entered by the player.
The while statement is similar to the do statement, except that the boolean expression is evaluated before execution of the enclosed statement. If the boolean expression evaluates to false, the while statement is terminated, and execution continues with the next statement following the while statement. The while statement syntax is as follows:
while (BooleanExpression) EnclosedStatement
A sample while statement is taken from the play() method of the BlackJackGame class:
while(dealersHand.mustHit())
dealersHand.addCard(deck.deal());
The while statement checks to see if the dealer must take a hit and, if so, adds a card to the dealer's hand. The while statement repeats this processing until the dealer is no longer required to take a hit.
The return statement is used to terminate execution of a method and return a value of the type specified in the method's declaration. Its syntax is
return Expression;
Expression must evaluate to a value that is compatible with the result type of the method in which it is used.
A sample return statement is taken from the getValue() method of the Card class:
int getValue() {
return iValue;
}
This simple method returns the value iValue and completes the execution of the getValue() method.
In this chapter you have toured the elements of the Java language by writing four sample programs. You have learned about the structure of Java programs, how to compile and execute them, and about many Java language elements. You should now be up and running with Java and capable of experimenting with it by writing your own programs. Although this chapter covers many elements of the Java syntax, use Chapter 11 as a complete reference for the Java language. Chapter 5 supplements the information you learned in this chapter with a solid background in Java's support of object-oriented programming.