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 C++-style comments begin with // and continue to the end of a line:
// Write to stdout.
The Java automated documentation support comments begin with
/** and end with
*/. They are found immediately before or after a Java declaration:
/**
* 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);
}
}
Save the file in your
c:\java\jdg\ch04 directory. Compile it with the command line
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
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 Deck Class
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
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
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.
Array Allocation
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.
|
Statement Blocks
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
The
if statement is used to decide whether a particular statement should be executed. Its syntax is as follows:
if ( BooleanExpression ) Statement1else 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
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
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
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
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
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
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.
No comments:
Post a Comment