By David Flanagan
Book Price: $39.95 USD
£28.50 GBP
PDF Price: $39.95
Cover | Table of Contents | Colophon
if/else branching
statement and the while and for looping statements. Learning to program
well is like learning to do word problems in high-school algebra class:
you have to translate the problem from an abstract description into the
concrete language of algebra (or, in this case, the language of Java).
Once you learn to think in if,
while, and for statements, other Java statements, such as
break, continue, switch, and try/catch/finally, should be easy to pick up.
Note that although Java is an object-oriented language, we won't discuss
objects until Chapter 2. package je3.basics; // A unique class name prefix
public class Hello { // Everything in Java is a class
public static void main(String[ ] args) { // All programs must have main( )
System.out.println("Hello World!"); // Say hello!
} // This marks the end of main( )
} // Marks the end of the classpackage je3.basics; // A unique class name prefix
public class Hello { // Everything in Java is a class
public static void main(String[ ] args) { // All programs must have main( )
System.out.println("Hello World!"); // Say hello!
} // This marks the end of main( )
} // Marks the end of the classpackage declaration. It specifies the name
of the package of which this program is part. The
program's name (as we'll see in the second line) is Hello. The package name is je3.basics. We can combine these two names
to produce a fully qualified name, je3.basics.Hello. Using packages provides a
unique namespace for every Java program. By placing this Hello program in a package, I've helped to
ensure that no naming conflict will arise if someone else defines a
program that is also named Hello.
Each chapter of this book has its own package that begins with the
prefix je3 (for JavaExamples3). In
this case, since this is the basics chapter, the package name is
je3.basics. Hello. It also says the class is public, which means it can be used by
anyone.FizzBuzz that plays a
version of the game. Actually, it isn't a very interesting version of
the game because the computer plays by itself, and it doesn't count in
French! What is interesting to us is the Java code that goes into this
example. It demonstrates the use of a for loop to count from 1 to 100 and the use
of if/else statements to decide
whether to output the number or one of the words "fizz", "buzz", or
"fizzbuzz". (In this case, the if/else statement is used as an if/elseif/elseif/else statement, as we'll
discuss shortly.) System.out.print( ). This method is just
like System.out.println( ), except
that it doesn't terminate the line of output. Whatever is output next
appears on the same line. /* and the characters */ is a comment in Java and ignored by the
compiler. When one of these comments begins with /**, as the one in this example does, then
it is additionally a doc comment, which means its
contents are used by the javadoc program that
automatically generates API documentation from Java source code.
package je3.basics;
/**
* This program plays the game "Fizzbuzz". It counts to 100, replacing each
* multiple of 5 with the word "fizz", each multiple of 7 with the word "buzz",
* and each multiple of both with the word "fizzbuzz". It uses the modulo
* operator (%) to determine if a number is divisible by another.
**/
public class FizzBuzz { // Everything in Java is a class
public static void main(String[ ] args) { // Every program must have main( )
for(int i = 1; i <= 100; i++) { // count from 1 to 100
if (((i % 5) == 0) && ((i % 7) == 0)) // Is it a multiple of 5 & 7?
System.out.print("fizzbuzz");
else if ((i % 5) == 0) // Is it a multiple of 5?
System.out.print("fizz");
else if ((i % 7) == 0) // Is it a multiple of 7?
System.out.print("buzz");
else System.out.print(i); // Not a multiple of 5 or 7
System.out.print(" ");
}
System.out.println( );
}
}for statement. It also
declares and uses variables to hold the previous two numbers in the
sequence, so that these numbers can be added together to produce the
next number in the sequence.package je3.basics;
/**
* This program prints out the first 20 numbers in the Fibonacci sequence.
* Each term is formed by adding together the previous two terms in the
* sequence, starting with the terms 1 and 1.
**/
public class Fibonacci {
public static void main(String[ ] args) {
int n0 = 1, n1 = 1, n2; // Initialize variables
System.out.print(n0 + " " + // Print first and second terms
n1 + " "); // of the series
for(int i = 0; i < 18; i++) { // Loop for the next 18 terms
n2 = n1 + n0; // Next term is sum of previous two
System.out.print(n2 + " "); // Print it out
n0 = n1; // First previous becomes 2nd previous
n1 = n2; // And current number becomes previous
}
System.out.println( ); // Terminate the line
}
}public static void main(String[ ] args)
main( ) method. What are
these strings, and where do they come from? The args array contains any arguments passed to
the Java interpreter on the command line, following the name of the
class to be run. Example 1-4
shows a program, Echo, that reads
these arguments and prints them back out. For example, you can invoke
the program this way:% java je3.basics.Echo this is a test
this is a test
args array has a length of four. The first
element in the array, args[0], is
the string "this", and the last element of the array, args[3], is "test". As you can see, Java
arrays begin with element 0. If you
are coming from a language that uses one-based arrays, this can take
quite a bit of getting used to. In particular, you must remember that
if the length of an array a is
n, the last element in the array is
a[n-1]. You can determine the
length of an array by appending .length to its name, as shown in Example 1-4.while loop. A while loop is a simpler form of the for loop; it requires you to do your own
initialization and update of the loop counter variable. Most for loops can be rewritten as a while loop, but the compact syntax of the
for loop makes it the more commonly
used statement. A for loop would
have been perfectly acceptable, and even preferable, in this
example.package je3.basics;
/**
* This program prints out all its command-line arguments.
**/
public class Echo {
public static void main(String[ ] args) {
int i = 0; // Initialize the loop variable
while(i < args.length) { // Loop until the end of array
System.out.print(args[i] + " "); // Print each argument out
i++; // Increment the loop variable
}
System.out.println( ); // Terminate the line
}
}Echo program of
Example 1-4, except that it
prints out the command-line arguments in reverse order, and it prints
out the characters of each argument backwards. Thus, the Reverse program can be invoked as follows,
with the following output:% java je3.basics.Reverse this is a test tset a si siht
for loops count backward instead of forward.
It is also interesting because it manipulates String objects by invoking methods of those
objects and the syntax starts to get a little complicated. For
example, consider the expression at the heart of this example:args[i].charAt(j)
ith element of the args[ ] array. We know from the declaration
of the array in the signature of the main(
) method that it is a String array; that is, it contains String objects. (Strings are not a primitive
type, like integers and boolean values in Java: they are full-fledged
objects.) Once you extract the ith
String from the array, you invoke
the charAt( ) method of that
object, passing the argument j.
(The . character in the expression refers to a method or a field of an
object.) As you can surmise from the name (and verify, if you want, in
a reference manual), this method extracts the specified character from
the String object. Thus, this
expression extracts the jth
character from the ith command-line
argument. Armed with this understanding, you should be able to make
sense of the rest of Example
1-5. package je3.basics;
/**
* This program echos the command-line arguments backwards.
**/
public class Reverse {
public static void main(String[ ] args) {
// Loop backwards through the array of arguments
for(int i = args.length-1; i >= 0; i--) {
// Loop backwards through the characters in each argument
for(int j=args[i].length( )-1; j>=0; j--) {
// Print out character j of argument i.
System.out.print(args[i].charAt(j));
}
System.out.print(" "); // Add a space at the end of each argument.
}
System.out.println( ); // And terminate the line when we're done.
}
}FizzBuzz
game. This version uses a switch
statement instead of nested if/else
statements to determine what its output should be for each number.
Take a look at the example first, then read the explanation of
switch.package je3.basics;
/**
* This class is much like the FizzBuzz class, but uses a switch statement
* instead of repeated if/else statements
**/
public class FizzBuzz2 {
public static void main(String[ ] args) {
for(int i = 1; i <= 100; i++) { // count from 1 to 100
switch(i % 35) { // What's the remainder when divided by 35?
case 0: // For multiples of 35...
System.out.print("fizzbuzz "); // print "fizzbuzz".
break; // Don't forget this statement!
case 5: case 10: case 15: // If the remainder is any of these
case 20: case 25: case 30: // then the number is a multiple of 5
System.out.print("fizz "); // so print "fizz".
break;
case 7: case 14: case 21: case 28: // For any multiple of 7...
System.out.print("buzz "); // print "buzz".
break;
default: // For any other number...
System.out.print(i + " "); // print the number.
break;
}
}
System.out.println( );
}
}switch statement
acts like a switch operator at a busy rail yard, switching a train (or
the execution of a program) to the appropriate track (or piece of
code) out of many potential tracks. A switch statement is often an alternative to
repeated if/else statements, but it
only works when the value being tested is an integer (i.e., long, float, double, boolean, and reference types such as
String objects are not allowed) and
when the value is being tested against constant values. The basic
syntax of the 5!, is the
product of 5*4*3*2*1, or 120. Example 1-7 shows a class, Factorial, that contains a method, factorial( ), that computes factorials. This
class is not a program in its own right, but the method it defines can
be used by other programs. The method itself is quite simple; we'll
see several variations of it in the following sections. As an
exercise, you might think about how you could rewrite this example
using a while loop instead of a
for loop.package je3.basics;
/**
* This class doesn't define a main( ) method, so it isn't a program by itself.
* It does define a useful method that we can use in other programs, though.
**/
public class Factorial {
/** Compute and return x!, the factorial of x */
public static int factorial(int x) {
if (x < 0) throw new IllegalArgumentException("x must be >= 0");
int fact = 1;
for(int i = 2; i <= x; i++) // loop
fact *= i; // shorthand for: fact = fact * i;
return fact;
}
}n! is equal to
n*(n-1)!. Computing factorials in
this fashion is a classic example of recursion. It is not a
particularly efficient technique in this case, but there are many
important uses for recursion, and this example demonstrates that it is
perfectly legal in Java. This example also switches from the int data type, which is a 32-bit integer, to
the long data type, which is a
64-bit integer. Factorials become very large, very quickly, so the
extra capacity of a long makes the
factorial( ) method more
useful.package je3.basics;
/**
* This class shows a recursive method to compute factorials. This method
* calls itself repeatedly based on the formula: n! = n * (n-1)!
**/
public class Factorial2 {
public static long factorial(long x) {
if (x < 0) throw new IllegalArgumentException("x must be >= 0");
if (x <= 1) return 1; // Stop recursing here
else return x * factorial(x-1); // Recurse by calling ourselves
}
}long data type. So, in this example, once a
factorial is computed, its value is stored for future use.Factorial3
class:static long[ ] table = new long[21]; static int last = 0;
factorial(
) method. This means that static fields can cache values
computed in one invocation for use by the next invocation.static long[ ] table = new long[21];
= sign) declares the static field table to be an array of long values. The second half of the line
actually creates an array of 21 long values using the new operator. throw new IllegalArgumentException("Overflow; x is too large.");new keyword, just as the
array was. When a program throws an exception object with the throw statement, it indicates that some sort
of unexpected circumstance or error has arisen. When an exception is
thrown, program control transfers to the nearest containing catch clause of a try/catch statement. This clause should
contain code to handle the exceptional condition. If an exception is
never caught, the program terminates with an error.20! is the largest factorial that can fit in
a 64-bit integer. But what if you want to compute 50! or 100!? The java.math.BigInteger class represents
arbitrarily large integer values and provides methods to perform
arithmetic operations on these very large numbers. Example 1-10 uses the BigInteger class to compute factorials of
any size. It also includes a simple main(
) method that defines a standalone test program for our
factorial( ) method. This test
program says, for example, that 50!
is the following 65-digit number:30414093201713378043612608166064768844377641568960512000000000000
import statement.
This statement must appear at the top of a Java file, before any class
is defined (but after the package
declaration). It provides a way to tell the compiler what classes you
are using in a program. Once a class like java.math.BigInteger has been imported, you
no longer have to type its full name; instead you can refer to it
simply as BigInteger. You can also
import an entire package of classes, as with the line:import java.util.*
java.lang package are automatically
imported, as are the classes of the current package, which, in this
case, is je3.basics.java.util.ArrayList class, which is a
utility class that implements an array-like data structure that can
grow to be as large as you need it to be. Because an ArrayList is an object rather than an array,
you use such methods as size( ),
add( ), and get( ) to work with it. By the same token, a
BigInteger is an object rather than
a primitive value, so you can't simply use the Integer.parseInt( ) method to convert a
string specified on the command line to a number. The program then
computes and prints the factorial of that number, using the Factorial4.factorial( ) method defined in
Example 1-10. That much is
simple; it takes only two lines of code. The rest of the example is
concerned with exception handling, or, in other words, taking care of
all of the things that can go wrong. You use the try/catch statement in Java for exception
handling. The try clause encloses a
block of code from which exceptions may be thrown. It is followed by
any number of catch clauses; the
code in each catch clause takes
care of a particular type of exception.try clause followed by
three catch clauses. Each clause
notifies the user about a particular error by printing an appropriate
message. This example is fairly straightforward. You may want to
consult Chapter 2 of
Java in a Nutshell, as it explains exceptions in
more detail.package je3.basics;
/**
* This program computes and displays the factorial of a number specified
* on the command line. It handles possible user input errors with try/catch.
**/
public class FactComputer {
public static void main(String[ ] args) {
// Try to compute a factorial.
// If something goes wrong, handle it in the catch clause below.
try {
int x = Integer.parseInt(args[0]);
System.out.println(x + "! = " + Factorial4.factorial(x));
}
// The user forgot to specify an argument.
// Thrown if args[0] is undefined.
catch (ArrayIndexOutOfBoundsException e) {
System.out.println("You must specify an argument");
System.out.println("Usage: java FactComputer <number>");
}
// The argument is not a number. Thrown by Integer.parseInt( ).
catch (NumberFormatException e) {
System.out.println("The argument you specify must be an integer");
}
// The argument is < 0. Thrown by Factorial4.factorial( )
catch (IllegalArgumentException e) {
// Display the message sent by the factorial( ) method:
System.out.println("Bad argument: " + e.getMessage( ));
}
}
}readLine(
) method of a BufferedReader object to do this. The line
that creates the BufferedReader may
look confusing. For now, take it on faith that it works; you don't
really need to understand how it works until we reach Chapter 3. Another feature of note in
Example 1-12 is the use of
the equals( ) method of the
String object line to check whether the user has typed
"quit".try clause. In
Example 1-12, however, there
is only a single catch clause to
handle the possible exceptions. This one handles any exception object
of type Exception. Exception is the superclass of all exception
types, so this one catch clause is
invoked no matter what type of exception is thrown.package je3.basics;
import java.io.*; // Import all classes in java.io package. Saves typing.
/**
* This program displays factorials as the user enters values interactively
**/
public class FactQuoter {
public static void main(String[ ] args) throws IOException {
// This is how we set things up to read lines of text from the user.
BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
// Loop forever
for(;;) {
// Display a prompt to the user
System.out.print("FactQuoter> ");
// Read a line from the user
String line = in.readLine( );
// If we reach the end-of-file,
// or if the user types "quit", then quit
if ((line == null) || line.equals("quit")) break;
// Try to parse the line, and compute and print the factorial
try {
int x = Integer.parseInt(line);
System.out.println(x + "! = " + Factorial4.factorial(x));
}
// If anything goes wrong, display a generic error message
catch(Exception e) { System.out.println("Invalid Input"); }
}
}
}String class that is used to
represent strings in Java is that it is immutable. In other words,
there are no methods that allow you to change the contents of a
string. Methods that operate on a string return a new string, not a
modified copy of the old one. When you want to operate on a string in
place, you must use a StringBuffer
object instead.StringBuffer. It interactively reads a line
of user input, as Example
1-12 did, and creates a StringBuffer to contain the line. The
program then encodes each character of the line using the
rot13 substitution cipher, which simply "rotates"
each letter 13 places through the alphabet, wrapping around from Z
back to A when necessary. Because a StringBuffer object is being used, you can
replace each character in the line one-by-one. A session with this
Rot13Input program might look like
this:% java je3.basics.Rot13Input > Hello there. Testing, testing! Uryyb gurer. Grfgvat, grfgvat! > quit %
main( ) method of Example 1-13 calls another method,
rot13( ), to perform the actual
encoding of a character. This method demonstrates the use of the
primitive Java char type and
character literals (i.e., characters that are used literally in a
program within single quotes).package je3.basics;
import java.io.*; // We're doing input, so import I/O classes
/**
* This program reads lines of text from the user, encodes them using the
* trivial "Rot13" substitution cipher, and then prints out the encoded lines.
**/
public class Rot13Input {
public static void main(String[ ] args) throws IOException {
// Get set up to read lines of text from the user
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
for(;;) { // Loop forever
System.out.print("> "); // Print a prompt
String line = in.readLine( ); // Read a line
if ((line == null) || line.equals("quit")) // If EOF or "quit"...
break; // ...break out of loop
StringBuffer buf = new StringBuffer(line); // Use a StringBuffer
for(int i = 0; i < buf.length( ); i++) // For each character...
buf.setCharAt(i, rot13(buf.charAt(i)));// ..read, encode, store
System.out.println(buf); // Print encoded line
}
}
/**
* This method performs the Rot13 substitution cipher. It "rotates"
* each letter 13 places through the alphabet. Since the Latin alphabet
* has 26 letters, this method both encodes and decodes.
**/
public static char rot13(char c) {
if ((c >= 'A') && (c <= 'Z')) { // For uppercase letters
c += 13; // Rotate forward 13
if (c > 'Z') c -= 26; // And subtract 26 if necessary
}
if ((c >= 'a') && (c <= 'z')) { // Do the same for lowercase letters
c += 13;
if (c > 'z') c -= 26;
}
return c; // Return the modified letter
}
}if statement within a for loop that is itself within another
for loop. You should take the time
to study this short program carefully. Make sure that you understand
exactly how it goes about sorting its array of numbers.package je3.basics;
/**
* This class demonstrates how to sort numbers using a simple algorithm
**/
public class SortNumbers {
/**
* This is a very simple sorting algorithm that is not very efficient
* when sorting large numbers of things
**/
public static void sort(double[ ] nums) {
// Loop through each element of the array, sorting as we go.
// Each time through, find the smallest remaining element, and move it
// to the first unsorted position in the array.
for(int i = 0; i < nums.length; i++) {
int min = i; // holds the index of the smallest element
// find the smallest one between i and the end of the array
for(int j = i; j < nums.length; j++) {
if (nums[j] < nums[min]) min = j;
}
// Now swap the smallest one with element i.
// This leaves all elements between 0 and i sorted.
double tmp;
tmp = nums[i];
nums[i] = nums[min];
nums[min] = tmp;
}
}
/** This is a simple test program for the algorithm above */
public static void main(String[ ] args) {
double[ ] nums = new double[10]; // Create an array to hold numbers
for(int i = 0; i < nums.length; i++) // Generate random numbers
nums[i] = Math.random( ) * 100;
sort(nums); // Sort them
for(int i = 0; i < nums.length; i++) // Print them out
System.out.println(nums[i]);
}
}package je3.basics;
/**
* This program computes prime numbers using the Sieve of Eratosthenes
* algorithm: rule out multiples of all lower prime numbers, and anything
* remaining is a prime. It prints out the largest prime number less than
* or equal to the supplied command-line argument.
**/
public class Sieve {
public static void main(String[ ] args) {
// We will compute all primes less than the value specified on the
// command line, or, if no argument, all primes less than 100.
int max = 100; // Assign a default value
try { max = Integer.parseInt(args[0]); } // Parse user-supplied arg
catch (Exception e) { } // Silently ignore exceptions.
// Create an array that specifies whether each number is prime or not.
boolean[ ] isprime = new boolean[max+1];
// Assume that all numbers are primes, until proven otherwise.
for(int i = 0; i <= max; i++) isprime[i] = true;
// However, we know that 0 and 1 are not primes. Make a note of it.
isprime[0] = isprime[1] = false;
// To compute all primes less than max, we need to rule out
// multiples of all integers less than the square root of max.
int n = (int) Math.ceil(Math.sqrt(max)); // See java.lang.Math class
// Now, for each integer i from 0 to n:
// If i is a prime, then none of its multiples are primes,
// so indicate this in the array. If i is not a prime, then
// its multiples have already been ruled out by one of the
// prime factors of i, so we can skip this case.
for(int i = 0; i <= n; i++) {
if (isprime[i]) // If i is a prime,
for(int j = 2*i; j <= max; j = j + i) // loop through multiples
isprime[j] = false; // they are not prime.
}
// Now go look for the largest prime:
int largest;
for(largest = max; !isprime[largest]; largest--) ; // empty loop body
// Output the result
System.out.println("The largest prime less than or equal to " + max +
" is " + largest);
}
}% java Substring hello 1 3
ell
SortNumbers class shows how you can sort
an array of doubles. Write a program that uses this class to sort
an array of 100 floating-point numbers. Then, interactively prompt
the user for numeric input, and display the next larger and next
smaller number from the array. You should use an efficient binary
search algorithm to find the desired position in the sorted
array.new operator, which
invokes a constructor of the class to initialize
the new object. The fields and methods of an object are accessed and
invoked using the . operator.static;
they operate on the class itself, rather than on an individual instance
of the class. Fields of a class may also be declared static, which makes them class fields instead
of instance fields. While each object has its own copy of each instance
field, there is only one copy of a class field and it is shared by all
instances of the class.public,
private, and protected. These different levels of
visibility allow fields and methods to be used in different contexts.
Every class has a superclass, from which it
inherits fields and methods. When a class inherits
from another class, it is called a subclass of that
class. Classes in Java form a class hierarchyRect class has four fields, x1, y1,
x2, and y2, that define the coordinates of the
corners of the rectangle. The Rect
class also defines a number of methods that operate on those
coordinates. toString( )
method. This method overrides the toString(
) method of java.lang.Object, which is the implicit
superclass of the Rect class.
toString( ) produces a String that represents a Rect object. As you'll see, this method is
quite useful for printing out Rect
values.package je3.classes;
/**
* This class represents a rectangle. Its fields represent the coordinates
* of the corners of the rectangle. Its methods define operations that can
* be performed on Rect objects.
**/
public class Rect {
// These are the data fields of the class
public int x1, y1, x2, y2;
/**
* The is the main constructor for the class. It simply uses its arguments
* to initialize each of the fields of the new object. Note that it has
* the same name as the class, and that it has no return value declared in
* its signature.
**/
public Rect(int x1, int y1, int x2, int y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
/**
* This is another constructor. It defines itself in terms of the above
**/
public Rect(int width, int height) { this(0, 0, width, height); }
/** This is yet another constructor. */
public Rect( ) { this(0, 0, 0, 0); }
/** Move the rectangle by the specified amounts */
public void move(int deltax, int deltay) {
x1 += deltax; x2 += deltax;
y1 += deltay; y2 += deltay;
}
/** Test whether the specified point is inside the rectangle */
public boolean isInside(int x, int y) {
return ((x >= x1)&& (x <= x2)&& (y >= y1)&& (y <= y2));
}
/**
* Return the union of this rectangle with another. I.e. return the
* smallest rectangle that includes them both.
**/
public Rect union(Rect r) {
return new Rect((this.x1 < r.x1) ? this.x1 : r.x1,
(this.y1 < r.y1) ? this.y1 : r.y1,
(this.x2 > r.x2) ? this.x2 : r.x2,
(this.y2 > r.y2) ? this.y2 : r.y2);
}
/**
* Return the intersection of this rectangle with another.
* I.e. return their overlap.
**/
public Rect intersection(Rect r) {
Rect result = new Rect((this.x1 > r.x1) ? this.x1 : r.x1,
(this.y1 > r.y1) ? this.y1 : r.y1,
(this.x2 < r.x2) ? this.x2 : r.x2,
(this.y2 < r.y2) ? this.y2 : r.y2);
if (result.x1 > result.x2) { result.x1 = result.x2 = 0; }
if (result.y1 > result.y2) { result.y1 = result.y2 = 0; }
return result;
}
/**
* This is a method of our superclass, Object. We override it so that
* Rect objects can be meaningfully converted to strings, can be
* concatenated to strings with the + operator, and can be passed to
* methods like System.out.println( )
**/
public String toString( ) {
return "[" + x1 + "," + y1 + "; " + x2 + "," + y2 + "]";
}
}RectTest
that puts the Rect class of Example 2-1 through its paces. Note
the use of the new keyword and the
Rect( ) constructor to create new
Rect objects. The program uses the
. operator to invoke methods of the Rect objects and to access their fields. The
test program also relies implicitly on the toString( ) method of Rect when it uses the string concatenation
operator (+) to create strings to
be displayed to the user.package je3.classes;
/** This class demonstrates how you might use the Rect class */
public class RectTest {
public static void main(String[ ] args) {
Rect r1 = new Rect(1, 1, 4, 4); // Create Rect objects
Rect r2 = new Rect(2, 3, 5, 6);
Rect u = r1.union(r2); // Invoke Rect methods
Rect i = r2.intersection(r1);
if (u.isInside(r2.x1, r2.y1)) // Use Rect fields and invoke a method
System.out.println("(" + r2.x1 + "," + r2.y1 +
") is inside the union");
// These lines implicitly call the Rect.toString( ) method
System.out.println(r1 + " union " + r2 + " = " + u);
System.out.println(r1 + " intersect " + r2 + " = " + i);
}
}Rect class
of Example 2-1. This DrawableRect class inherits the fields and
methods of Rect and adds its own
method, draw( ), that draws a
rectangle using a specified java.awt.Graphics object. (We'll see more of
the Graphics object in Chapter 12.) DrawableRect also defines a constructor that
simply uses the super keyword to
pass its arguments up to the corresponding Rect constructor. Note the use of the
extends keyword to indicate that
Rect is the superclass of DrawableRect.package je3.classes;
/**
* This is a subclass of Rect that allows itself to be drawn on a screen.
* It inherits all the fields and methods of Rect
* It relies on the java.awt.Graphics object to perform the drawing.
**/
public class DrawableRect extends Rect {
/** The DrawableRect constructor just invokes the Rect( ) constructor */
public DrawableRect(int x1, int y1, int x2, int y2) { super(x1,y1,x2,y2); }
/** This is the new method defined by DrawableRect */
public void draw(java.awt.Graphics g) {
g.drawRect(x1, y1, (x2 - x1), (y2-y1));
}
}ColoredRect
is a subclass of DrawableRect (see
Example 2-3), which makes it
a sub-subclass of Rect (see Example 2-1). This class inherits
the fields and methods of DrawableRect and of Rect (and of Object, which is the implicit superclass of
Rect). ColoredRect adds two new fields that specify
the border color and fill color of the rectangle when it is drawn.
(These fields are of type java.awt.Color, which we'll learn about in
Chapter 12.) The class also
defines a new constructor that allows these fields to be initialized.
Finally, ColoredRect overrides the
draw( ) method of the DrawableRect class. The draw( ) method defined by ColoredRect draws a rectangle using the
specified colors, rather than simply using the default colors as the
method in DrawableRect did.package je3.classes;
import java.awt.*;
/**
* This class subclasses DrawableRect and adds colors to the rectangle it draws
**/
public class ColoredRect extends DrawableRect {
// These are new fields defined by this class.
// x1, y1, x2, and y2 are inherited from our super-superclass, Rect.
protected Color border, fill;
/**
* This constructor uses super( ) to invoke the superclass constructor, and
* also does some initialization of its own.
**/
public ColoredRect(int x1, int y1, int x2, int y2,
Color border, Color fill)
{
super(x1, y1, x2, y2);
this.border = border;
this.fill = fill;
}
/**
* This method overrides the draw( ) method of our superclass so that it
* can make use of the colors that have been specified.
**/
public void draw(Graphics g) {
g.setColor(fill);
g.fillRect(x1, y1, (x2-x1), (y2-y1));
g.setColor(border);
g.drawRect(x1, y1, (x2-x1), (y2-y1));
}
}ComplexNumber class defines two double fields, which represent the real and
imaginary parts of the number. These fields are declared private, which means they can be used only
within the body of the class; they are inaccessible outside the class.
Because the fields are inaccessible, the class defines two accessor
methods, real( ) and imaginary( ), that simply return their
values. This technique of making fields private and defining accessor methods is
called encapsulation. Encapsulation hides the
implementation of a class from its users, which means that you can
change the implementation without it affecting the users.ComplexNumber
class doesn't define any methods, other than the constructor, that set
the values of its fields. Once a ComplexNumber object is created, the number
it represents can never be changed. This property is known as
immutability; it is often useful to design
objects that are immutable like this. ComplexNumber
defines two add( ) methods and two
multiply( ) methods that perform
addition and multiplication of complex numbers. The difference between
the two versions of each method is that one is an instance method and
one is a class, or static, method. Consider the add( ) methods, for example. The instance
method adds the value of the current instance of ComplexNumber to another specified ComplexNumber object. The class method
doesn't have a current instance; it simply adds the values of two
specified ComplexNumber objects.
The instance method is invoked through an instance of the class, like
this:ComplexNumber sum = a.add(b);
addDatum( ) method,
the Averager class updates its
internal state so that its other methods can easily return the average
and standard deviation of the numbers that have been passed to it so
far. Although this Averager
class does not model any "thing," we've followed the
Java naming convention of