![]() |
||
![]() |
![]() Alden Hosting provides professional, efficient, and reliable business-class Web hosting and Website Design services. |
|
JAVA, JSP, SERVLETS, TOMCAT, SERVLETS MANAGER, |
•10 Tips for Killer Website Design •7 Sure shots ways to improve your website •Attracting Visitors and Improving Your Search Results •Chasing the Search Engines Algorithms •Crash Course in Getting a 1 Google Ranking •Design Your Site for Traffic in 2005 •Designing A Website That Sells •Googles Good Writing Content Filter •How to Write Effective Web Copy •How to Write Title Tags for Your Web Pages •JSP Scripting Elements and Variables •JSP •Java How to Send Email in Java •Java MySQL Database Connection •Make Money Fast With Google Adwords •Make Money On The Internet What Is Your Niche •Make Money Quick With Google Adsense •Ranked 1 at Google for Invisible Entrepreneurs But No Traffic •Ruby Classes Objects and Variables •Ruby Containers Blocks and Iterators •SEO One Way Web Links 5 Strategies •SEO Success Step Two Attracting Search Engine Attention •The 10 Best Resources for CSS •The 3 Best Website Traffic Sources •The 5 Biggest Mistakes Almost All Web Designers Make •The Five Ways You Should Be Using Keywords •The Three Principles Of Image Optimization •Top 5 Secrets to Making Money with Adsense •True Paid Inclusion Programs are a Thing of the Past •Understanding Web Logs And Why it Matters
|
Web Hosting Tips for Webmasters - |
|
|
I
with five elements; i.e. the type of the array is int
and the array has dimension 5.
Like all other variables in Java an array must be declared. When
you declare an array variable you suffix the type with
[]
to indicate that this variable is an array. Here are
some examples:
int[] k;
float[] yt;
String[] names;
In other words you declare an array like you'd declare any other
variable except you append brackets to the end of the variable
type.
new
operator. When we create an array we
need to tell the compiler how many elements will be stored in it.
Here's how we'd create the variables declared above:
k = new int[3];
yt = new float[7];
names = new String[50];
The numbers in the brackets specify the dimension of the
array; i.e. how many slots it has to hold values. With the
dimensions above k
can hold three ints, yt
can hold seven floats and
names can hold fifty Strings. Therefore this step is sometimes
called dimensioning the array. More commonly this is
called allocating the array since this step actually sets
aside the memory in RAM that the array requires.
This is also our first look at the new
operator. new
is a reserved word in java that is used
to allocate not just an array, but also all kinds of objects. Java
arrays are full-fledged objects with all that implies. For now the
main thing it implies is that we have to allocate them with
new
.
k
above has elements k[0]
,
k[1]
, and k[2]
.
Since we started counting at zero there is no k[3]
,
and trying to access it will generate an
ArrayIndexOutOfBoundsException
.
You can use array elements wherever you'd use a similarly typed variable that wasn't part of an array.
Here's how we'd store values in the arrays we've been working with:
k[0] = 2;
k[1] = 5;
k[2] = -2;
yt[6] = 7.5f;
names[4] = "Fred";
This step is called initializing the array or, more precisely,
initializing the elements of the array. Sometimes the phrase
"initializing the array" would be reserved for when we
initialize all the elements of the array.
For even medium sized arrays, it's unwieldy to specify each element
individually. It is often helpful to use for
loops to
initialize the array. For instance here is a loop that fills an
array with the squares of the numbers from 0 to 100.
float[] squares = new float[101];
for (int i=0; i <= 100; i++) {
squares[i] = i*i;
}
Two things you should note about this code fragment:
i
is an int it becomes a float when it is stored in squares,
since we've declared squares to be an array of floats.
float[] squares = new float[101];
for (int i=0, i < squares.length; i++) {
squares[i] = i*i;
}
Note that the <= changed to a < to make this work.
We can declare and allocate an array at the same time like this:
int[] k = new int[3];
float[] yt = new float[7];
String[] names = new String[50];
We can even declare, allocate, and initialize an array at the same time providing
a list of the initial values inside brackets like so:
int[] k = {1, 2, 3};
float[] yt = {0.0f, 1.2f, 3.4f, -9.87f, 65.4f, 0.0f, 567.9f};
args
.
As our second example let's consider a class that counts the
occurrences of the digits 0-9 in decimal expansion of the number pi,
for example. This is an issue of some interest both to pure number
theorists and to theologians. See, for example,
We will do this by creating an array of ten longs called
ndigit
. The zeroth element of ndigit
will
track the number of zeroes in the input stream; the first element
of ndigit
will track the numbers of 1's and so on.
We'll test Java's random number generator and see if it produces
apparently random numbers.
import java.util.*;
class RandomTest {
public static void main (String args[]) {
int[] ndigits = new int[10];
double x;
int n;
Random myRandom = new Random();
// Initialize the array
for (int i = 0; i < 10; i++) {
ndigits[i] = 0;
}
// Test the random number generator a whole lot
for (long i=0; i < 100000; i++) {
// generate a new random number between 0 and 9
x = myRandom.nextDouble() * 10.0;
n = (int) x;
//count the digits in the random number
ndigits[n]++;
}
// Print the results
for (int i = 0; i <= 9; i++) {
System.out.println(i+": " + ndigits[i]);
}
}
}
We've got three for
loops in this code, one to initialize the array, one to perform
the desired calculation, and a final one to print out the results. This is quite common
in code that uses arrays.
c0 |
c1 |
c2 |
c3 |
|
r0 |
0 |
1 |
2 |
3 |
r1 |
1 |
2 |
3 |
4 |
r2 |
2 |
3 |
4 |
5 |
r3 |
3 |
4 |
5 |
6 |
r4 |
4 |
5 |
6 |
7 |
Here we have an array with five rows and four columns. It has twenty total elements. However we say it has dimension four by five, not dimension twenty. This array is not the same as a five by four array like this one:
c0 |
c1 |
c2 |
c3 |
c4 |
|
r0 |
0 |
1 |
2 |
3 |
4 |
r1 |
1 |
2 |
3 |
4 |
5 |
r2 |
2 |
3 |
4 |
5 |
6 |
r3 |
3 |
4 |
5 |
6 |
7 |
We need to use two numbers to identify a position in a two-dimensional array. These are the element's row and column positions. For instance if the above array is called J then J[0][0] is 0, J[0][1] is 1, J[0][2] is 2, J[0][3] is 3, J[1][0] is 1, and so on.
Here's how the elements in a four by five array called M are referred to:
M[0][0] | M[0][1] | M[0][2] | M[0][3] | M[0][4] |
M[1][0] | M[1][1] | M[1][2] | M[1][3] | M[1][4] |
M[2][0] | M[2][1] | M[2][2] | M[2][3] | M[2][4] |
M[3][0] | M[3][1] | M[3][2] | M[3][3] | M[3][4] |
for
loops to
fill the array.
The array examples above are filled with the sum of their row and column indices. Here's some code that would create and fill such an array:
class FillArray {
public static void main (String args[]) {
int[][] M;
M = new int[4][5];
for (int row=0; row < 4; row++) {
for (int col=0; col < 5; col++) {
M[row][col] = row+col;
}
}
}
}
Of course the algorithm you would use to fill the array depends completely
on the use to which the array is to be put. Here is a program which calculates
the identity matrix for a given dimension. The identity matrix of dimension
N is a square matrix which contains ones along the diagonal and zeros in all
other positions.
class IDMatrix {
public static void main (String args[]) {
double[][] ID;
ID = new double[4][4];
for (int row=0; row < 4; row++) {
for (int col=0; col < 4; col++) {
if (row != col) {
ID[row][col]=0.0;
}
else {
ID[row][col] = 1.0;
}
}
}
}
}
In two-dimensional arrays ArrayIndexOutOfBounds errors occur
whenever you exceed the maximum column index or row index. Unlike
two-dimensional C arrays, two-dimensional Java arrays are not just
one-dimensional arrays indexed in a funny way.
The syntax for three dimensional arrays is a direct extension of that for two-dimensional arrays. Here's a program that declares, allocates and initializes a three-dimensional array:
class Fill3DArray {
public static void main (String args[]) {
int[][][] M;
M = new int[4][5][3];
for (int row=0; row < 4; row++) {
for (int col=0; col < 5; col++) {
for (int ver=0; ver < 3; ver++) {
M[row][col][ver] = row+col+ver;
}
}
}
}
}
We need three nested for
loops here to handle the extra
dimension.
The syntax for still higher dimensions is similar. Just add another pair pf brackets and another dimension.
Unless you have some special knowledge of the contents of the array
(for instance, that it is sorted) the quickest algorithm for
searching an array is straight-forward linear search. Use a
for
loop to look at every element of the array until
you find the element you want. Here's a simple method that prints
the largest and smallest elements of an array:
static void printLargestAndSmallestElements (int[] n) {
int max = n[0];
int min = n[0];
for (int i=1; i < n.length; i++) {
if (max < n[i]) {
max = n[i];
}
if (min > n[i]) {
min = n[i];
}
}
System.out.println("Maximum: " + max);
System.out.println("Minimum: " + min);
return;
}
If you're going to search an array many times, you may want to sort the array,
before searching it. We'll discuss sorting algorithms in the next section.
a
and b
.
Most people propose something like this as the solution:
class Swap1 {
public static void main(String args[]) {
int a = 1;
int b = 2;
System.out.println("a = "+a);
System.out.println("b = "+b);
// swap a and b
a = b;
b = a;
System.out.println("a = "+a);
System.out.println("b = "+b);
}
}
This produces the following output:
a = 1 b = 2 a = 2 b = 2That isn't what you expected! The problem is that we lost track of the value 1 when we put the value of
b
into a
. To correct
this we need to introduce a third variable, temp
, to hold the
original value of a
.
class Swap2 {
public static void main(String args[]) {
int a = 1;
int b = 2;
int temp;
System.out.println("a = "+a);
System.out.println("b = "+b);
// swap a and b
temp = a;
a = b;
b = temp;
System.out.println("a = "+a);
System.out.println("b = "+b);
}
}
This code produces the output we expect:
a = 1 b = 2 a = 2 b = 1
import java.util.*;
class BubbleSort {
public static void main(String args[]) {
int[] n;
n = new int[10];
Random myRand = new Random();
// initialize the array
for (int i = 0; i < 10; i++) {
n[i] = myRand.nextInt();
}
// print the array's initial order
System.out.println("Before sorting:");
for (int i = 0; i < 10; i++) {
System.out.println("n["+i+"] = " + n[i]);
}
boolean sorted = false;
// sort the array
while (!sorted) {
sorted = true;
for (int i=0; i < 9; i++) {
if (n[i] > n[i+1]) {
int temp = n[i];
n[i] = n[i+1];
n[i+1] = temp;
sorted = false;
}
}
}
// print the sorted array
System.out.println();
System.out.println("After sorting:");
for (int i = 0; i < 10; i++) {
System.out.println("n["+i+"] = " + n[i]);
}
}
}
In this case we have sorted the array in ascending order,
smallest element first. It would be easy to change this to
sort in descending order.
// This is the Hello program in Java
class Hello {
public static void main (String args[]) {
/* Now let's say hello */
System.out.print("Hello ");
System.out.println(args[0]);
}
}
Do you remember what happened when you ran the program
without giving it any command line arguments? The runtime
system generated an exception
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException at Hello.main(C:\javahtml\Hello.java:7)
What happened was that since we didn't give Hello any command
line arguments there wasn't anything in args[0]
.
Therefore Java kicked back this not too friendly error message
about an "ArrayIndexOutOfBoundsException."
Previously we fixed this problem by testing the length of the array before we tried to access its first element. This worked well in this simple case, but this is far from the only such potential problem. If you were to check for every possible error condition in each line of code, you would find your code becoming bloated with more error checking than actual code. Moreover you then have to start checking for errors in the error conditions. In older languages like C, Basic and Fortran you sooner or later you find yourself resorting to goto or longjmp or other such ingredients in the recipe for illegible Spaghetti Code
The goal of exception handling is to be able to define the regular flow of the program in part of the code without worrying about all the special cases. Then, in a separate block of code, you cover the exceptional cases. This produces more legible code since you don't need to interrupt the flow of the algorithm to check and respond to every possible strange condition. The runtime environment is responsible for moving from the regular program flow to the exception handlers when an exceptional condition arises.
In practice what you do is write blocks of code that may generate exceptions inside try-catch blocks. You try the statements that generate the exceptions. Within your try block you are free to act as if nothing has or can go wrong. Then, within one or more catch blocks, you write the program logic that deals with all the special cases.
Here's an example of exception handling in Java using the Hello World program above:
// This is the Hello program in Java
class ExceptionalHello {
public static void main (String args[]) {
/* Now let's say hello */
try {
System.out.println("Hello " + args[0]);
}
catch (Exception e) {
System.out.println("Hello whoever you are");
}
}
}
Some exceptions need to be caught and dealt with while others
are generally considered to be so horrific that the runtime
system, just gives up. The compiler will complain if you write
code that doesn't watch out for the not-too-dangerous
exceptions, but you'll need to watch out for the really
dangerous ones (like ArrayIndexOutOfBoundsExceptions)
yourself.
Sometimes catching an exception can feel like a little yippy dog
that finally catches a car. Now that it's got hold of the
bumper between its teeth, what is it going to do with it? Many
times there may not be much you can do. Bad exceptions stop the
program by default. This is at least preferable to unhandled
exceptions in most programming languages where the entire system
can come crashing down around your feet with a core dump or
worse. Sometimes you may want to this too. In that case you
can call the System.exit(int)
method to halt your
running program.
Other times you may just break out of a loop you were in and continue with the rest of your code. This is most common when an exception isn't really unexpected or when it doesn't badly affect your program logic.
You may or may not print an error message. If you write an exception handler and you don't expect it to be called, then by all means put a
System.out.println("Error: " + e);
in your exception handler. That way if something does go wrong (and something always does) you'll at least know where it went wrong. However don't put an error message in exception handlers you expect to be exercised in the course of normal program flow. Remember, they're exceptions, not errors.
In the next section we'll see lots of examples of code that can possibly generate exceptions which must be caught. We'll talk more about exceptions in a later chapter, but this is the bare bones you need to know to respond to the exceptions generated by the java.io classes we'll be discussing in the next section.
Streams are a big topic in Java and later we'll devote an entire
chapter to them. For now we want to cover just the very basics
that let you write files, read files and communicate with the
user. In fact the System.out.println()
statement
we've been using all along is an implementation of Streams.
import java.io.*;
class PersonalHello {
public static void main (String args[])
{
byte name[] = new byte[100];
int nr_read = 0;
System.out.println("What is your name?");
try {
nr_read = System.in.read(name);
System.out.print("Hello ");
System.out.write(name,0,nr_read);
}
catch (IOException e) {
System.out.print("I'm Sorry. I didn't catch your name.");
}
}
}
In code that does any significant input or output you'll want to
begin by importing all the various java.io classes.
import.java.io.*;
does this and is as ubiquitous in
Java applications as #include <stdio.h>
is in C
programs.
Most of the reading and writing you do in Java will be done with bytes. (As you'll see later there are also ways to read text files directly into Strings.) Here we've started with an array of bytes that will hold the user's name.
First we print a query requesting the user's name. Then we read the
user's name using the System.in.read()
method. This
method takes a byte array as an argument, and places whatever the
user types in that byte array. Then, like before, we print
"Hello." Finally we print the user's name.
The program doesn't actually see what the user types until he or she types a carriage return. This gives the user the chance to backspace over and delete any mistakes. Once the return key is pressed, everything in the line is placed in the array.
What happens if the user types more than 100 characters of text
before hitting a carriage return? In many programming languages
this would lead to a rather nasty program crash. It's also the sort
of bug that often gets out the door in a shipping product since
programmers often fail to test their programs against extreme
inputs. However Java has been programmed a little more safely.
System.in.read()
won't read past the end of the array
even though we didn't explicitly check to make sure the input was
sufficiently small. My guess is that the
System.in.read()
method internally checks the length of
the array it's been passed using the name.length property.
Next we promised to write a getNextInteger()
method that will accept an integer from
the user. Here it is:
static int getNextInteger() {
String line;
DataInputStream in = new DataInputStream(System.in);
try {
line = in.readLine();
int i = Integer.valueOf(line).intValue();
return i;
}
catch (Exception e) {
return -1;
}
} // getNextInteger ends here
// Write the Fahrenheit to Celsius table in a file
import java.io.*;
class FahrToCelsius {
public static void main (String args[]) {
double fahr, celsius;
double lower, upper, step;
lower = 0.0; // lower limit of temperature table
upper = 300.0; // upper limit of temperature table
step = 20.0; // step size
fahr = lower;
try {
FileOutputStream fout = new FileOutputStream("test.out");
// now to the FileOutputStream into a PrintStream
PrintStream myOutput = new PrintStream(fout);
while (fahr <= upper) { // while loop begins here
celsius = 5.0 * (fahr-32.0) / 9.0;
myOutput.println(fahr + " " + celsius);
fahr = fahr + step;
} // while loop ends here
} // try ends here
catch (IOException e) {
System.out.println("Error: " + e);
System.exit(1);
}
} // main ends here
}
There are only three things necessary to write formatted output to a file rather than to the
standard output:
FileOutputStream fout = new FileOutputStream("test.out");
This line initializes the FileOutputStream with the name of the file you want to write into.
PrintStream myOutput = new PrintStream(fout);
The PrintStream is passed the FileOutputStream from step 1.
System.out.println()
use myOutput.println()
.
System.out
and myOutput
are just different instances of the
PrintStream
class. To print to a different PrintStream
we keep
the syntax the same but change the name of the PrintStream
.
// Imitate the Unix cat utility
import java.io.*;
class cat {
public static void main (String args[]) {
String thisLine;
//Loop across the arguments
for (int i=0; i < args.length; i++) {
//Open the file for reading
try {
FileInputStream fin = new FileInputStream(args[i]);
// now turn the FileInputStream into a DataInputStream
try {
DataInputStream myInput = new DataInputStream(fin);
try {
while ((thisLine = myInput.readLine()) != null) { // while loop begins here
System.out.println(thisLine);
} // while loop ends here
}
catch (Exception e) {
System.out.println("Error: " + e);
}
} // end try
catch (Exception e) {
System.out.println("Error: " + e);
}
} // end try
catch (Exception e) {
System.out.println("failed to open file " + args[i]);
System.out.println("Error: " + e);
}
} // for end here
} // main ends here
}
FahrToCelsius is a very basic application that could be written in almost any programming language from the most ancient machine code to the most advanced LISP machine. Nonetheless it is important to notice that Java solves this problem just as easily as would a language more commonly associated with numeric and scientific programming such as Fortran or C. In fact this code was translated almost verbatim from Kernighan and Ritchie. Only a few minor semantic changes were required to produce a valid, efficient Java program. Although Java has many, many features that make it suitable for complex object-oriented applications, it is also fully suitable for classic numerical programs, something that is not true of competitors like SmallTalk or LISP.
In fact Java can outperform even Fortran and C in numerical applications when precision, reliability and portability are more important than speed. Java's true arrays with bounds checking and its well-defined IEEE 754 floating point data types are especially useful in this regard.
Since there is as of yet no native architecture compiler for
Java, it's too early to throw away your Fortran manuals. CPU
intensive applications will still be coded in Fortran. The
Java language itself (as opposed to its implementation) is
also lacking in a couple of important respects for numeric
computation. The lack of an exponentiation operator like
Fortran's **
and the lack of a complex data
type are both troublesome to longtime Fortran programmers.
However neither is insurmountable.
Would anyone care to write a Fortran to Java translator?
In the next chapter we'll move beyond the 1970's to discuss more modern features of Java including Objects, Applets, Event Driven Programming and threads.
In this chapter we're going to move into the 1980's. In particular we're going to work with event driven programming. This style of programming should be very familiar to Macintosh and Windows programmers. In those environments program logic doesn't flow from the top to the bottom of the program as it does in most procedural code. Rather the operating system collects events and the program responds to them. These events may be mouse clicks, keypresses, network data arriving on the Ethernet port, or any of about two dozen other possibilities. The operating system looks at each event, determines what program it was intended for, and places the event in the appropriate program's event queue.
Every application program has an event loop.
This is just a while
loop which loops
continuously. On every pass through the loop the
application retrieves the next event from its event queue
and responds accordingly.
Java applets behave similarly. However the runtime environment (i.e. the browser) takes care of the event loop for the applet so there's no need to write one explicitly. Rather you need to have methods in your applet subclass that respond to each kind of event you want to process.
This is all fairly abstract until you see some concrete examples. Let's begin with a simple one.
import java.applet.Applet;
import java.awt.Graphics;
public class HelloWorldApplet extends Applet {
public void paint(Graphics g) {
g.drawString("Hello world!", 50, 25);
}
}
This applet version of HelloWorld is a little more complicated than the
HelloWorld application of the previous chapter, and it will take a little
more effort to run it as well.
First type in the source code and save it into file called HelloWorldApplet.java in the javahtml/classes directory. Compile this file by typing javac HelloWorldApplet.java at the command line prompt.
If all is well a file called HelloWorldApplet.class will be created. This file must be in your classes directory.
Now you need to create an HTML file that will include your applet. The following simple HTML file will do.
<HTML>
<HEAD>
<TITLE> Hello World </TITLE>
</HEAD>
<BODY>
This is the applet:<P>
<APPLET codebase="classes" code="HelloWorldApplet.class" width=200 height=200 ></APPLET>
</BODY>
</HTML>
Save this file as "HelloWorldApplet.html" in the javahtml directory.
When you've done that load the HTML file
into a Java enabled browser such as HotJava or Netscape 2.0. You should see the following:
This is the applet: Hello World!
If the applet compiled without error and produced a HelloWorldApplet.class file, and yet you don't see the string "Hello World" in your browser chances are that the class file is in the wrong place. Make sure the .html file is in the javahtml directory and the compiled .class file is in the javahtml/classes directory.
import java.applet.Applet;
import java.awt.Graphics;
The import
statement in Java is similar to the #include statement in C
or C++. It pulls in the classes that are contained in a package
elsewhere. A package is merely a collection of related classes. In
this case we're requesting access to the public classes contained in the
basic release, java.applet.Applet and java.awt.Graphics.
Depending on the phase of the moon,
awt stands for
"advanced window toolkit" or "applet window toolkit". You'll see a lot more of it.
The next change from the application is the Class definition:
public class HelloWorldApplet extends Applet
The extends keyword indicates that this class is a subclass of the Applet class; or, to put it another way, Applet is a superclass of HelloWorldApplet. The Applet class is defined in the java.applet.Applet package which we just imported. Since HelloWorldApplet is a subclass of the Applet class, our HelloWorldApplet automatically inherits all the functionality of the generic Applet class. Anything an Applet can do, the HelloWorldApplet can do too.
The next difference between the applet and the application is far less obvious (except maybe to a longtime C programmer). There's no main method! Applets don't need them. The main method is actually in the browser or the AppletViewer, not in the Applet itself. Applets are like plugin code modules for Adobe Photoshop that provide extra functionality, but can't run without a main program to host them.
Rather than starting at a specific place in the code applets are
event driven. An applet waits for one of a series of
events such as a key press, the mouse pointer being moved over the
applets visible area, or a mouse click and then executes the
appropriate event handler. Since this is our first program we only
have one event handler, paint
.
Most applets need to handle the paint
event. This event
occurs whenever a part of the applet's visible area is uncovered and
needs to be drawn again.
The paint method is passed a Graphics
object which
we've chosen to call g. The Graphics class is defined in the
java.awt.Graphics package which we've imported. Within the paint
method we call g's drawString method to draw the string "Hello
World!" at the coordinates (50,25). That's 50 pixels across and
twenty-five pixels down from the upper left hand corner of the
applet. We'll talk more about coordinate systems later. This
drawing takes place whenever a portion of the screen containing our
applet is covered and then uncovered and needs to be refreshed.
For reasons that remain a mystery to HTML authors everywhere, but possibly having something to do with packages and classpaths, if the applet resides somewhere other than the same directory as the page it lives on, then you don't just give a URL to its location. Rather you point at the directory where the .class file is using the CODEBASE parameter. You still also have to use CODE to give name of the .class file.
Also like IMG, APPLET has several parameters to define how it is positioned on the page. HEIGHT and WIDTH parameters work exactly as they do with IMG, specifying how big a rectangle the browser should leave for the applet. These numbers are specified in pixels. ALIGN also works exactly as for images (in those browsers that support ALIGN) defining how the applet's rectangle is placed on the page relative to other elements. Possible values include LEFT, RIGHT, TOP, TEXTTOP, MIDDLE, ABSMIDDLE, BASELINE, BOTTOM and ABSBOTTOM. Finally as with IMG you can specify an HSPACE and a VSPACE in pixels to set the amount of blank space between an applet and the surrounding text.
Finally also like IMG, APPLET has an ALT tag. As far as I know ALT is not yet implemented in any browsers. An ALT tag is used by a browser that understands the APPLET tag but for some reason cannot play the applet. For instance if an applet needs to write a file on your hard drive, but your preferences are set not to allow that, then the browser should display the ALT text.
ALT is not used for browsers that do not understand APPLET at all. For that purpose APPLET has been defined to include a closing tag as well, </APPLET>. All raw text between the opening and closing APPLET tags is ignored by a Java capable browser. However a non-Java capable browser will ignore the APPLET tags instead and read the text between them.
To demonstrate this we'll convert HelloWorldApplet into a generic string drawing applet. To do this we'll need to pass the applet parameters that define the string to be drawn.
import java.applet.Applet;
import java.awt.Graphics;
public class DrawStringApplet extends Applet {
String input_from_page;
public void init() {
input_from_page = getParameter("String");
}
public void paint(Graphics g) {
g.drawString(input_from_page, 50, 25);
}
}
Now you need to create an HTML file that will include your applet.
The following simple HTML file will do:
<HTML>
<HEAD>
<TITLE> Draw String </TITLE>
</HEAD>
<BODY>
This is the applet:<P>
<APPLET codebase="classes" code="DrawStringApplet.class" width=200 height=200><PARAM name="String" value="Howdy, there!"></APPLET>
</BODY>
</HTML>
Of course you are free to change "Howdy, there!" to a string of your choice.
Note that this allows you to change the output of the applet without changing or recompiling the code.
You're not limited to one parameter either. You can pass as many named parameters to an applet as you like.
The getParameter
method is straightforward. You give it a
string that's the name of the parameter you want. You get
back a string that's the value of the parameter. All
parameters are passed as Strings. If you want to get
something else like an integer then you'll need to pass it
as a String and convert it into the type you really want.
The PARAM
HTML tag is also straightforward. It occurs
between <APPLET>
and </APPLET>
. It has two
parameters of its own, NAME and VALUE. The NAME identifies
which parameter this is for the getParameter
method. VALUE
is the value of the parameter as a String. Both must be
enclosed in double quote marks like all other HTML tag
parameters.
import java.applet.Applet;
import java.awt.*;
public class EventTutor extends Applet {
public void init() {
System.out.println("init event");
}
public void paint(Graphics g) {
System.out.println("paint event");
}
public void start() {
System.out.println("start event");
}
public void destroy() {
System.out.println("destroy event");
}
public void update(Graphics g) {
System.out.println("update event");
}
public boolean mouseUp(Event e, int x, int y) {
System.out.println("mouseUp event");
return false;
}
public boolean mouseDown(Event e, int x, int y) {
System.out.println("mouseDown event");
return false;
}
public boolean mouseDrag(Event e, int x, int y) {
System.out.println("mouseDrag event");
return false;
}
public boolean mouseMove(Event e, int x, int y) {
System.out.println("mouseMove event");
return false;
}
public boolean mouseEnter(Event e, int x, int y) {
System.out.println("mouseEnter event");
return false;
}
public boolean mouseExit(Event e, int x, int y) {
System.out.println("mouseExit event");
return false;
}
public void getFocus() {
System.out.println("getFocus event");
}
public void gotFocus() {
System.out.println("gotFocus event");
}
public void lostFocus() {
System.out.println("lostFocus event");
}
public boolean keyDown(Event e, int x) {
System.out.println("keyDown event");
return true;
}
}
Once you've compiled and loaded this applet play with it. Click
the mouse in the applet window. Doubleclick the mouse. Click
and drag. Type some text. Resize the browser window. Cover it
and then uncover it. Keep your eye on the standard output (Java
console in Netscape) while doing this.
Here are some questions to answer:
import java.awt.*
.
This time we need more than one class from the awt package
so rather than worrying about which one to import, we just
get them all with the *
. The compiler is smart enough to
only link in those that it actually uses.
Finally there are a whole lot of new event methods. We'll cover them in detail in the next section. For now see under what circumstances you can make each one happen.
System.out.println()
in an applet. On some
systems this may not work at all. However it has the
advantage of being familiar and easy. For more serious work
you should actually draw your text in the applet window.
There are at least three different ways to do this. For our
purposes the one that makes the most sense is to use a
List.
A List is a scrolling list of Strings defined in
java.awt.List
. We create a new List with new, just as we
create any other Object. The specific constructor we use
asks for an int that's the number of visible lines and a
boolean that tells whether or not multiple selections are
allowed. We'll ask for 25 lines and no multiple
selections.
List theList;
theList = new List(25, false);
We add Strings to the list by using the addItem method of
the List we're adding to like so:
theList.addItem("This is a list item");
Finally we need to actually add this List to our applet
(more precisely the applet's container). We do this with the
line
add(theList);
in the init method.
That's all. We can use the same applet we used before with these simple changes.
import java.applet.Applet;
import java.awt.*;
public class EventList extends Applet {
List theList;
public void init() {
theList = new List(25, false);
add(theList);
theList.addItem("init event");
}
public void paint(Graphics g) {
theList.addItem("paint event");
}
public void start() {
theList.addItem("start event");
}
public void destroy() {
theList.addItem("destroy event");
}
public void update(Graphics g) {
theList.addItem("update event");
}
public boolean mouseUp(Event e, int x, int y) {
theList.addItem("mouseUp event");
return false;
}
public boolean mouseDown(Event e, int x, int y) {
theList.addItem("mouseDown");
return false;
}
public boolean mouseDrag(Event e, int x, int y) {
theList.addItem("mouseDrag event");
return false;
}
public boolean mouseMove(Event e, int x, int y) {
theList.addItem("mouseMove event");
return false;
}
public boolean mouseEnter(Event e, int x, int y) {
theList.addItem("mouseEnter event");
return false;
}
public boolean mouseExit(Event e, int x, int y) {
theList.addItem("mouseExit event");
return false;
}
public void getFocus() {
theList.addItem("getFocus event");
}
public void gotFocus() {
theList.addItem("gotFocus event");
}
public void lostFocus() {
theList.addItem("lostFocus event");
}
public boolean keyDown(Event e, int x) {
theList.addItem("keyDown event");
return true;
}
}
We'll talk more about containers, Lists, and applet components in a
later section.
init()
method is called when your applet begins executing.
Netscape is also known to call this method at other times such as
when an applet is reloaded or you return to a page containing an
applet. Generally you use this method to set up any data structures
or perform any tasks you need to get ready to run the applet. Since
it's only called once it's easy to miss the init()
method in the
EventTutor applet. If necessary redirect the standard output to a
file and look at the first line of that file to see it.
public void init() {
System.out.println("init event");
}
paint()
method. Almost any applet is going to
need to override this method. This is the method in which you will
do all your drawing. You can only write to the applet screen in the
paint method. However there are times when you'll want to write to
an offscreen image in another method and then just quickly copy that
image to the screen in your paint()
method.
public void paint(Graphics g) {
theList.addItem("paint event");
}
stop()
message says the user is no longer looking at the page that
contains the applet. This is usually because the user left the page
or minimized the window. At this time you should stop any CPU eating
activities that don't matter when the user isn't looking at your
page. For instance your Doom applet should stop tracking monster
movement if the user isn't actually playing. On the other hand a
spreadsheet applet in the middle of a long calculation should
continue calculating because the user is likely to want the result
later. Once the user returns to the page the start()
method is
called.
public void stop() {
theList.addItem("stop event");
}
public void start() {
theList.addItem("start event");
}
public void destroy() {
theList.addItem("destroy event");
}
update()
method is called automatically by the system when ????.
It's often overridden when you want to use offscreen Images to avoid flicker.
public void update(Graphics g) {
theList.addItem("update event");
}
mouseUp()
method is called whenever the mouse
button is released in your applet. In most cases
this is the event you'll want to watchout for, not mouseDown. A
button is typically highlighted when the mouse button is pressed on
it, but it is not activated till the user releases the mouse button.
This gives the user a chance to change their mind by moving the
cursor off the object without releasing it.The exception would be when you want an action to continue as long as the mouse button is held down, a fast forward button on a movie playing applet for instance.
mouseUp()
methods also receive the
coordinates of the point where the mouse was released.
public boolean mouseUp(Event e, int x, int y) {
theList.addItem("mouseUp event at (" + x + "," + y + ")");
return false;
}
mouseDown()
method is called whenever the mouse
button is pressed in your applet. In most cases
you'll want to wait for a mouseUp before taking any action
though. mouseDown()
methods also receive the coordinates of the
point where the mouse was released.
public boolean mouseDown(Event e, int x, int y) {
theList.addItem("mouseDown event at (" + x + "," + y + ")");
return false;
}
mouseDrag()
methods occur when a user moves the mouse
while holding down the mouse button. mouseDrag()
methods receive the coordinates of the point where the mouse is when
the event occurs.
public boolean mouseDrag(Event e, int x, int y) {
theList.addItem("mouseDrag event at (" + x + "," + y + ")");
return false;
}
public boolean mouseMove(Event e, int x, int y) {
theList.addItem("mouseMove event at (" + x + "," + y + ")");
return false;
}
public boolean mouseEnter(Event e, int x, int y) {
theList.addItem("mouseEnter event at " + x + "," + y + ")");
return false;
}
public boolean mouseExit(Event e, int x, int y) {
theList.addItem("mouseExit event at (" + x + "," + y + ")");
return false;
}
public void getFocus() {
theList.addItem("getFocus event");
}
public void lostFocus() {
theList.addItem("lostFocus event");
}
public boolean keyDown(Event e, int x) {
theList.addItem("The " + (char) x + " key was pressed.");
return false;
}
Here's the revised EventTutor Applet:
In the first applet we'll just draw a rectangle on the screen. We'll get the size of the applet as specified in the HTML file, and then we'll draw a rectangle around the applet to frame it. Here's the code:
//Draw a rectangle
import java.applet.*;
import java.awt.*;
public class Mondrian1 extends Applet {
int height, width;
public void init() {
Dimension d = size();
height = d.height;
width = d.width;
repaint();
}
public void paint(Graphics g) {
g.drawRect(0, 0, width, height);
}
}
Compile this applet; move the resulting class file to your
classes directory, and create an HTML file that points to
it. Set the height of the applet to 300 pixels and the
width to 300 pixels as well. Load that file into your
browser and what do you see? Probably not what you expected.
You should see half a rectangle. What happened to the
other half?
This is called a fencepost error. The applet lives in a square 300 pixels tall by 300 pixels wide. However the upper left hand corner of the applet starts at (0, 0), not at (1, 1). This means that the applet includes the points with x and y coordinates between 0 and 299, not between 0 and 300. We drew a rectangle 301 pixels high and 301 pixels wide so the edges were chopped off.
This is fortuitous however. Not only does it allow me the opportunity to digress on fencepost errors (which, although annoying, are far less dangerous in Java than in C since Java does check array boundaries) but it also shows us something else. In Java the coordinate system for an applet begins in the upper left hand corner and increases to the right and down. This is common in computer graphics but is different from the Cartesian coordinate system where the direction of increasing y is generally assumed to be up.
Correcting the fence post error is easy. We just change
g.drawRect(0, 0, width, height);
to
g.drawRect(0, 0, width-1, height-1);
//Draw a rectangle
import java.applet.*;
import java.awt.*;
public class Mondrian2 extends Applet {
int height, width;
public void init() {
Dimension d = size();
height = d.height;
width = d.width;
repaint();
}
public void paint(Graphics g) {
g.drawRect(0, 0, width-1, height-1);
}
}
As usual compile this and load it into your browser. If the problem isn't fixed check to
make sure that you moved the new class file into the classes directory and that you modified the HTML file to point to Mondrian2.
We've introduced exactly one new statement in all this code, drawRect
which is
a method in the Graphics class. The line g.drawRect(0, 0, height-1, width-1)
instructs the Graphics class g
to draw a rectangle beginning at the point
(0, 0) and ending at the point (299, 299).
This particular rectangles encompasses the entire applet's visible space. There is nothing to keep us from drawing outside the applet, in fact we did exactly that in our first version where we actually extended the rectangle to (300, 300); but anything we draw there won't be seen by the user.
The drawRect method draws an open rectangle. If we want to draw a filled rectangle we use the fillRect method. Otherwise the syntax is identical. In Mondrian3 we'll draw a filled rectangle in the center of the applet. Here's the code:
//Draw a rectangle
import java.applet.*;
import java.awt.*;
public class Mondrian3 extends Applet {
int AppletHeight;
int AppletWidth;
int RectHeight;
int RectWidth;
int RectTop;
int RectLeft;
public void init() {
Dimension d = size();
AppletHeight = d.height;
AppletWidth = d.width;
RectHeight = AppletHeight/3;
RectWidth = AppletWidth/3;
RectTop = (AppletHeight - RectHeight)/2;
RectLeft= (AppletWidth - RectWidth)/2;
repaint();
}
public void paint(Graphics g) {
g.drawRect(0, 0, AppletWidth-1, AppletHeight-1);
g.fillRect(RectLeft, RectTop, RectWidth-1, RectHeight-1);
}
}
This latest example also demonstrates one other thing. Until now you may have thought
that we were passing two points to the drawRect and fillRect methods and drawing the rectangle
that joins them. This is how rectangles are implemented in QuickDraw on the Mac for example. However if that was the case the preceding rectangle would have been drawn between (100, 100)
and (100, 100), a fairly small rectangle. Since that isn't the case our association of
the last two variables with width and height must be correct.
The extremely astute reader may object at this point. Until now we've only drawn squares. Although the last two variables passed to drawRect and fillRect must be the height and the width how do we know which is which? The simplest way to tell is to write a test program that draws a non-square rectangle. Let's try that now:
//Draw a rectangle
import java.applet.Applet;
import java.awt.*;
public class Mondrian4 extends Applet {
int RectHeight, RectWidth, RectTop, RectLeft, AppletWidth, AppletHeight;
public void init() {
Dimension d = size();
AppletHeight = d.height;
AppletWidth = d.width;
RectHeight = AppletHeight/3;
RectWidth = (AppletWidth*2)/3;
RectTop = (AppletHeight - RectHeight)/2;
RectLeft= (AppletWidth - RectWidth)/2;
repaint();
}
public void paint(Graphics g) {
g.drawRect(0, 0, AppletWidth-1, AppletHeight-1);
g.fillRect(RectLeft, RectTop, RectWidth-1, RectHeight-1);
}
}
So you see that the third argument is indeed the width and
the fourth is the height.
Now that we've learned how to draw rectangles, both filled and unfilled, let's make life a little more exciting by randomly selecting the position and size of the rectangle. To do this we'll need the Math.random() method from java.lang.Math. This method returns a double between 0.0 and 1.0 so we'll need to multiply the result by the applet's height and width to get a reasonably sized rectangle that fits into our applet space. To do this we'll create the following Randomize method:
private int Randomize( int range )
{
double rawResult;
rawResult = Math.random();
return (int) (rawResult * range);
}
This method forces the result of Math.random into an int
in the range we require.
Pay special attention to the last line. When you see a raw type in parentheses like (int)
or (float)
it's a cast.
Casts change one value type into another.
Thus here we're changing a double
into an int
. The cast rounds as necessary.
Casting in Java is safer than in C or other languages that
allow arbitrary casting. Java only lets casts occur when
they make sense, such as a cast between a float
and an int
. However you can't cast between an
int
and a String
for example.
//Draw a rectangle
import java.applet.Applet;
import java.awt.*;
public class Mondrian5 extends Applet {
int RectHeight, RectWidth, RectTop, RectLeft, AppletWidth, AppletHeight;
public void init() {
Dimension d = size();
AppletHeight = d.height;
AppletWidth = d.width;
RectTop = Randomize(AppletHeight);
RectLeft= Randomize(AppletWidth);
RectHeight = Randomize(AppletHeight - RectTop);
RectWidth = Randomize(AppletWidth - RectLeft);
repaint();
}
public void paint(Graphics g) {
g.drawRect(0, 0, AppletWidth-1, AppletHeight-1);
g.fillRect(RectLeft, RectTop, RectWidth-1, RectHeight-1);
}
private int Randomize(int range)
{
double rawResult;
rawResult = Math.random();
return (int) (rawResult * range);
}
}
Occasionally this applet does randomly produce a rectangle that's two small to see
so if you don't see anything, reload it.
Reload it a few times. Each time you'll see a rectangle of
a different size appear in a different place.
Let's make our world a little more colorful. To do this we'll change the rectangle color to red. To do this we'll use a new methods setColor(), part of the Graphics class.
//Draw a colored rectangle
import java.applet.Applet;
import java.awt.*;
public class Mondrian6 extends Applet {
int RectHeight, RectWidth, RectTop, RectLeft, AppletWidth, AppletHeight;
public void init() {
Dimension d = size();
AppletHeight = d.height;
AppletWidth = d.width;
RectTop = Randomize(AppletHeight);
RectLeft= Randomize(AppletWidth);
RectHeight = Randomize(AppletHeight - RectTop);
RectWidth = Randomize(AppletWidth - RectLeft);
repaint();
}
public void paint(Graphics g) {
// g.setBackground(Color.white);
g.setColor(Color.red);
g.drawRect(0, 0, AppletWidth-1, AppletHeight-1);
g.fillRect(RectLeft, RectTop, RectWidth-1, RectHeight-1);
}
private int Randomize(int range)
{
double rawResult;
rawResult = Math.random();
return (int) (rawResult * range);
}
}
The awt predefines a number of colors including:
Color(127, 127, 127)
. Pure white
would be Color(255, 255, 255)
. Pure red is
(255, 0, 0)
and so on.
By using the color constructor we can expand our program to select not only a random rectangle but also a random color for the rectangle. Here's the code:
//Draw a randomly colored rectangle
import java.applet.Applet;
import java.awt.*;
public class Mondrian7 extends Applet {
int RectHeight, RectWidth, RectTop, RectLeft, AppletWidth, AppletHeight;
Color RectColor;
public void init() {
Dimension d = size();
AppletHeight = d.height;
AppletWidth = d.width;
RectTop = Randomize(AppletHeight);
RectLeft= Randomize(AppletWidth);
RectHeight = Randomize(AppletHeight - RectTop);
RectWidth = Randomize(AppletWidth - RectLeft);
RectColor = new Color(Randomize(255),Randomize(255),Randomize(255));
repaint();
}
public void paint(Graphics g) {
// g.setBackground(Color.white);
g.setColor(RectColor);
g.drawRect(0, 0, AppletWidth-1, AppletHeight-1);
g.fillRect(RectLeft, RectTop, RectWidth-1, RectHeight-1);
}
private int Randomize(int range)
{
double rawResult;
rawResult = Math.random();
return (int) (rawResult * range);
}
}
In the next example we're going to draw multiple randomly sized, randomly colored
rectangles. Since we want each rectangle to be different we're going to have to move the
calculation of the rectangle's shape, position and color into the paint() method. Here's the code:
//Draw many randomly colored rectangles
import java.applet.Applet;
import java.awt.*;
public class Mondrian8 extends Applet {
int RectHeight, RectWidth, RectTop, RectLeft, AppletWidth, AppletHeight;
Color RectColor;
int numberRectangles = 100;
public void init() {
Dimension d = size();
AppletHeight = d.height;
AppletWidth = d.width;
repaint();
}
public void paint(Graphics g) {
g.setColor(Color.black);
g.drawRect(0, 0, AppletWidth-1, AppletHeight-1);
for (int i=0; i < numberRectangles; i++) {
RectTop = Randomize(AppletHeight);
RectLeft= Randomize(AppletWidth);
RectHeight = Randomize(AppletHeight - RectTop);
RectWidth = Randomize(AppletWidth - RectLeft);
RectColor = new Color(Randomize(255),Randomize(255),Randomize(255));
g.setColor(RectColor);
g.fillRect(RectLeft, RectTop, RectWidth-1, RectHeight-1);
}
}
private int Randomize(int range)
{
double rawResult;
rawResult = Math.random();
return (int) (rawResult * range);
}
}
Finally let's let the HTML specify the number of rectangles to be drawn in one pass. We'll keep the default value as is and only replace it if the HTML includes a Number PARAM.
//Draw many random rectangles
import java.applet.Applet;
import java.awt.*;
public class Mondrian9 extends Applet {
int RectHeight, RectWidth, RectTop, RectLeft, AppletWidth, AppletHeight;
Color RectColor;
int numberRectangles = 100;
public void init() {
Dimension d = size();
AppletHeight = d.height;
AppletWidth = d.width;
String s = getParameter("Number");
if (s != null) {
numberRectangles = Integer.valueOf(s).intValue();
}
repaint();
}
public void paint(Graphics g) {
g.setColor(Color.black);
g.drawRect(0, 0, AppletWidth-1, AppletHeight-1);
for (int i=0; i < numberRectangles; i++) {
RectTop = Randomize(AppletHeight);
RectLeft= Randomize(AppletWidth);
RectHeight = Randomize(AppletHeight - RectTop);
RectWidth = Randomize(AppletWidth - RectLeft);
RectColor = new Color(Randomize(255),Randomize(255),Randomize(255));
g.setColor(RectColor);
g.fillRect(RectLeft, RectTop, RectWidth-1, RectHeight-1);
}
}
private int Randomize(int range)
{
double rawResult;
rawResult = Math.random();
return (int) (rawResult * range);
}
}
That's all for now, but we'll return to Mondrian at the end
of this chapter when we add threading, and show how to draw
rectangles continuously.
import java.applet.Applet;
import java.awt.*;
public class SimpleLine extends Applet {
int AppletHeight, AppletWidth;
public void init() {
Dimension d = size();
AppletHeight = d.height;
AppletWidth = d.width;
}
public void paint(Graphics g) {
g.drawLine(0, 0, AppletWidth, AppletHeight);
}
}
drawLine()
method to draw considerably
non-straight figures. It is shown in advanced calculus that
any reasonably well-behaved (should that be differentiable?)
function can be approximated arbitrarily well by straight
lines where quantities like &well-behaved" and
"arbitrarily are precisely defined. I'll spare you the
details of the mathematical proof, but I will demonstrate
its probability to you by producing an applet that does a
very good job of graphing any function you care to throw at
it. As usual we'll develop it in pieces rather than just
throwing it all out at once.
We begin with the skeleton applet. We'll need to add some code to the paint method of the applet to make it draw something. Let's begin by drawing a sine wave from the left hand side of the image to the right hand side. Here's the complete program:
import java.applet.*;
import java.awt.*;
public class GraphApplet extends Applet {
int x0, xN, y0, yN;
public void init() {
// How big is the applet?
Dimension d = size();
x0 = 0;
xN = d.width-1;
y0=0;
yN=d.height-1;
}
public void paint(Graphics g) {
for (int x = x0; x < xN; x++) {
g.drawLine(x,(int) (yN*Math.sin(x)),x+1, (int) (yN*Math.sin(x+1)));
}
}
}
The meat of this applet is in the for loop of the paint method.
for (int x = x0; x < xN; x++) {
g.drawLine(x,(int) (yN*Math.sin(x)),x+1, (int) (yN*Math.sin(x+1)));
}
Here we loop across every x pixel of the applet. At each one
we calculate the sine of that pixel. We also calculate the
sine of the next pixel. This gives us two 2-D points and we
draw a line between them. Since the sine of a real number
is always between one and negative one, we scale the y value
by yN. Finally we cast the y values to ints since sines are
fundamentally floating point values but drawLine requires
ints.
This applet runs but it's got a lot of problems. All of them can be related to two factors:
We'll need a method that will convert a point in the applet window into a point in the Cartesian plane, and one that will convert it back. Here it is:
import java.applet.*;
import java.awt.*;
public class GraphApplet extends Applet {
int x0, xN, y0, yN;
double xmin, xmax, ymin, ymax;
int AppletHeight, AppletWidth;
public void init() {
// How big is the applet?
Dimension d = size();
AppletHeight = d.height;
AppletWidth = d.width;
x0 = 0;
xN = AppletWidth-1;
y0=0;
yN=AppletHeight-1;
xmin = -10.0;
xmax = 10.0;
ymin = -1.0;
ymax = 1.0;
}
public void paint(Graphics g) {
double x1,y1,x2,y2;
int i, j1, j2;
j1 = yvalue(0);
for (i = 0; i < AppletWidth; i++) {
j2 = yvalue(i+1);
g.drawLine(i, j1 ,i+1, j2);
j1 = j2;
}
}
private int yvalue(int ivalue) {
// Given the xpoint we're given calculate the Cartesian equivalent
double x, y;
int jvalue;
x = (ivalue * (xmax - xmin)/(AppletWidth - 1)) + xmin;
// Take the sine of that x
y = Math.sin(x);
// Scale y into window coordinates
jvalue = (int) ((y - ymin)*(AppletHeight - 1)/(ymax - ymin));
// Switch jvalue from cartesian coordinates to computer graphics coordinates
jvalue = AppletHeight - jvalue;
return jvalue;
}
}
Run this applet. Isn't that a much nicer looking sine wave?
There are still a number of things we can add to make this
a more complete applet though. The most important would be
to add some parameters so that we can define the size of the
applet in HTML. The following modification of the init and
paint methods looks for xmin, xmax, ymin, and ymax to be
specified via parameters. However for robustness if the
author of the HTML forgets to specify them we supply some
reasonable default values.
import java.applet.*;
import java.awt.*;
public class GraphApplet extends Applet {
int x0, xN, y0, yN;
double xmin, xmax, ymin, ymax;
int AppletHeight, AppletWidth;
public void init() {
String ParamString;
// How big is the applet?
Dimension d = size();
AppletHeight = d.height;
AppletWidth = d.width;
x0 = 0;
xN = AppletWidth-1;
y0=0;
yN=AppletHeight-1;
ParamString = getParameter("xmin");
if (ParamString != null) {
xmin = Double.valueOf(ParamString).doubleValue();
}
else {
xmin = -1.0;
}
ParamString = getParameter("xmax");
if (ParamString != null) {
xmax = Double.valueOf(ParamString).doubleValue();
}
else {
xmax = 1.0;
}
ParamString = getParameter("ymax");
if (ParamString != null) {
ymax = Double.valueOf(ParamString).doubleValue();
}
else {
ymax = 1.0;
}
ParamString = getParameter("ymin");
if (ParamString != null) {
ymin = Double.valueOf(ParamString).doubleValue();
}
else {
ymin = -1.0;
}
}
public void paint(Graphics g) {
double x1,y1,x2,y2;
int i, j1, j2;
j1 = yvalue(0);
for (i = 0; i < AppletWidth; i++) {
j2 = yvalue(i+1);
g.drawLine(i, j1 ,i+1, j2);
j1 = j2;
}
}
private int yvalue(int ivalue) {
// Given the xpoint we're given calculate the Cartesian equivalent
double x, y;
int jvalue;
x = (ivalue * (xmax - xmin)/(AppletWidth - 1)) + xmin;
// Take the sine of that x
y = Math.sin(x);
// Scale y into window coordinates
jvalue = (int) ((y - ymin)*(AppletHeight - 1)/(ymax - ymin));
// Switch jvalue from cartesian coordinates to computer graphics coordinates
jvalue = AppletHeight - jvalue;
return jvalue;
}
}
Now we can adjust the range over which we graph without
modifying our code!
So far we've only graphed sine functions. It should be obvious how to modify the code to graph cosines or many other kinds of functions. However what if we want to define the function at runtime?
The middle third set is defined by starting with all the real numbers between zero and one inclusive. Then we cut out the middle third of that set (exclusive of the endpoints). i.e. everything between one third and two thirds exclusive.
Next we cut the middle third of the two line segments that remain, i.e. everything between one ninth and two ninths and between seven ninths and eight ninths. We continue this process indefinitely.
Was that confusing? Good. A picture is worth a thousand words and a good Java program is worth a thousand pictures. We now proceed to show you a Java program that draws successive pictures to demonstrate the middle third set.
import java.applet.Applet;
import java.awt.*;
import java.util.Vector;
public class MiddleThird extends Applet {
int AppletWidth;
int AppletHeight;
Vector endpoints = new Vector();
public void init() {
Dimension d = size();
AppletHeight = d.height;
AppletWidth = d.width;
endpoints.addElement(new Float(0.0f));
endpoints.addElement(new Float(1.0f));
}
public void paint(Graphics g) {
float x1, x2;
Float tempFloat;
for (int i = 0; i < AppletHeight; i+= 5) {
// draw the lines
for (int j=0; j < endpoints.size(); j += 2) {
tempFloat = (Float) endpoints.elementAt(j);
x1 = tempFloat.floatValue();
tempFloat = (Float) endpoints.elementAt(j+1);
x2 = tempFloat.floatValue();
g.drawLine( Math.round(x1*AppletWidth), i, Math.round(x2*AppletWidth), i);
}
//remove the middle third of the lines
CutSegments();
// Now check to see if we've exceeded the resolution of our screen
tempFloat = (Float) endpoints.elementAt(0);
x1 = tempFloat.floatValue();
tempFloat = (Float) endpoints.elementAt(1);
x2 = tempFloat.floatValue();
if (Math.round(x1*AppletWidth) == Math.round(x2*AppletWidth)) break;
}
}
private void CutSegments() {
int index = 0;
float gap;
float x1, x2;
Float tempFloat1, tempFloat2;
int stop = endpoints.size();
for (int i=0; i < stop; i+=2) {
CutMiddleThird(index, index+1);
index += 4;
}
}
private void CutMiddleThird(int left, int right) {
float gap;
float x1, x2;
Float tempFloat1, tempFloat2;
tempFloat1 = (Float) endpoints.elementAt(left);
tempFloat2 = (Float) endpoints.elementAt(right);
gap = tempFloat2.floatValue() - tempFloat1.floatValue();
x1 = tempFloat1.floatValue() + gap/3.0f;
x2 = tempFloat2.floatValue() - gap/3.0f;
endpoints.insertElementAt(new Float(x2), right);
endpoints.insertElementAt(new Float(x1), right);
}
}
Compile and load this applet. Is that clearer? Of course
this isn't a perfect representation of the middle third set
since we have to deal with points of finite size rather than
with genuine mathematical points. Depending on how large a
window you give your applet, you will probably only see
about six to twelve iterations before we need to start
working with fractional pixels.
//Bounce lines around in a box
import java.applet.Applet;
import java.awt.*;
public class FlyingLines extends Applet {
int NUM_LINES = 25;
int gDeltaTop=3, gDeltaBottom=3;
int gDeltaLeft=2, gDeltaRight=6;
int AppletWidth, AppletHeight;
int gLines[][] = new int[NUM_LINES][4];
public void init() {
AppletWidth = size().width;
AppletHeight = size().height;
}
public void start() {
gLines[0][0] = Randomize(AppletWidth);
gLines[0][1] = Randomize(AppletHeight);
gLines[0][2] = Randomize(AppletWidth);
gLines[0][3] = Randomize(AppletHeight);
for (int i=1; i < NUM_LINES; i++ ) {
LineCopy(i, i-1);
RecalcLine(i);
}
repaint();
}
public void paint(Graphics g) {
while (true) {
for (int i=NUM_LINES - 1; i > 0; i--) {
LineCopy(i, i-1);
}
RecalcLine(0);
g.setColor(Color.black);
g.drawLine(gLines[0][0], gLines[0][1], gLines[0][2], gLines[0][3]);
g.setColor(getBackground());
g.drawLine(gLines[NUM_LINES-1][0], gLines[NUM_LINES-1][1],
gLines[NUM_LINES-1][2], gLines[NUM_LINES-1][3]);
}
}
private void LineCopy (int to, int from) {
for (int i = 0; i < 4; i++) {
gLines[to][i] = gLines[from][i];
}
}
public int Randomize( int range ) {
double rawResult;
rawResult = Math.random();
return (int) (rawResult * range);
}
private void RecalcLine( int i ) {
gLines[i][1] += gDeltaTop;
if ((gLines[i][1] < 0) || (gLines[i][1] > AppletHeight)) {
gDeltaTop *= -1;
gLines[i][1] += 2*gDeltaTop;
}
gLines[i][3] += gDeltaBottom;
if ( (gLines[i][3] < 0) || (gLines[i][3] > AppletHeight) ) {
gDeltaBottom *= -1;
gLines[i][3] += 2*gDeltaBottom;
}
gLines[i][0] += gDeltaLeft;
if ( (gLines[i][0] < 0) || (gLines[i][0] > AppletWidth) ) {
gDeltaLeft *= -1;
gLines[i][0] += 2*gDeltaLeft;
}
gLines[i][2] += gDeltaRight;
if ( (gLines[i][2] < 0) || (gLines[i][2] > AppletWidth) ) {
gDeltaRight *= -1;
gLines[i][2] += 2*gDeltaRight;
}
} //RecalcLine ends here
} // FlyingLines ends here
The paint loops in both Mondrian and FlyingLines are ideal for a thread, a separate stream of execution that takes place simultaneously and independently of everything else that might be happening (like responding to the programmer's insistence to "Quit!, Damnit!"). Without threads an entire program can be held up by one CPU intensive task or, as in Flying Lines, one infinite loop, intentional or otherwise.
As a general rule all CPU intensive tasks should be placed in their own threads. Here's one way to do it.
//Draw infinitely many random rectangles
import java.applet.Applet;
import java.awt.*;
public class ThreadedMondrian extends Applet implements Runnable {
int RectHeight, RectWidth, RectTop, RectLeft, AppletWidth, AppletHeight;
Color RectColor;
Thread kicker = null;
int pause;
public void init() {
Dimension d = size();
AppletHeight = d.height;
AppletWidth = d.width;
repaint();
}
public void paint(Graphics g) {
g.setColor(Color.black);
g.drawRect(0, 0, AppletWidth-1, AppletHeight-1);
for (int i=0; i < 10; i++) {
RandomRect();
g.setColor(RectColor);
g.fillRect(RectLeft, RectTop, RectWidth-1, RectHeight-1);
}
}
public void run() {
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
while (true) { // infinite loop
repaint();
try {
Thread.sleep(100);
}
catch (Exception e) {
}
}
}
public void start() {
if (kicker == null) {
kicker = new Thread(this);
kicker.start();
}
}
public void stop() {
kicker = null;
}
public void RandomRect() {
RectTop = Randomize(AppletHeight);
RectLeft = Randomize(AppletWidth);
RectHeight= Randomize(AppletHeight - RectTop);
RectWidth = Randomize(AppletWidth - RectLeft);
RectColor = new Color(Randomize(255),Randomize(255),Randomize(255));
}
private int Randomize(int range)
{
double rawResult;
rawResult = Math.random();
return (int) (rawResult * range);
}
}
We did added four key things to Mondrian to make it threaded and a lot more CPU friendly.
Let's look at them in more detail:
//Bounce lines around in a box
import java.applet.Applet;
import java.awt.*;
public class FlyingLines extends Applet implements Runnable {
int NUM_LINES = 25;
int gDeltaTop=3, gDeltaBottom=3;
int gDeltaLeft=2, gDeltaRight=6;
int AppletWidth, AppletHeight;
int gLines[][] = new int[NUM_LINES][4];
public void init() {
AppletWidth = size().width;
AppletHeight = size().height;
}
public void start() {
gLines[0][0] = Randomize(AppletWidth);
gLines[0][1] = Randomize(AppletHeight);
gLines[0][2] = Randomize(AppletWidth);
gLines[0][3] = Randomize(AppletHeight);
for (int i=1; i < NUM_LINES; i++ ) {
LineCopy(i, i-1);
RecalcLine(i);
}
repaint();
Thread t = new Thread(this);
t.start();
}
public void run () {
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
while (true) {
for (int i=NUM_LINES - 1; i > 0; i--) {
LineCopy(i, i-1);
}
RecalcLine(0);
System.out.println(gLines[0][0] + ", " + gLines[0][1] + "," + gLines[0][2] + ", " + gLines[0][3]);
repaint();
try {
Thread.currentThread().sleep(10);
}
catch (Exception e) {
}
}
}
public void paint(Graphics g) {
for (int i=0; i < NUM_LINES; i++) {
g.drawLine(gLines[i][0], gLines[i][1], gLines[i][2], gLines[i][3]);
}
}
private void LineCopy (int to, int from) {
for (int i = 0; i < 4; i++) {
gLines[to][i] = gLines[from][i];
}
}
public int Randomize( int range ) {
double rawResult;
rawResult = Math.random();
return (int) (rawResult * range);
}
private void RecalcLine( int i ) {
gLines[i][1] += gDeltaTop;
if ((gLines[i][1] < 0) || (gLines[i][1] > AppletHeight)) {
gDeltaTop *= -1;
gLines[i][1] += 2*gDeltaTop;
}
gLines[i][3] += gDeltaBottom;
if ( (gLines[i][3] < 0) || (gLines[i][3] > AppletHeight) ) {
gDeltaBottom *= -1;
gLines[i][3] += 2*gDeltaBottom;
}
gLines[i][0] += gDeltaLeft;
if ( (gLines[i][0] < 0) || (gLines[i][0] > AppletWidth) ) {
gDeltaLeft *= -1;
gLines[i][0] += 2*gDeltaLeft;
}
gLines[i][2] += gDeltaRight;
if ( (gLines[i][2] < 0) || (gLines[i][2] > AppletWidth) ) {
gDeltaRight *= -1;
gLines[i][2] += 2*gDeltaRight;
}
} //RecalcLine ends here
} // FlyingLines ends here
class BozoSortAlgorithm extends SortAlgorithm {
void sort(int a[]) {
boolean sorted = false;
while (!sorted) {
int index1 = Randomize(a.length);
int index2 = Randomize(a.length);
int temp = a[index2];
a[index2] = a[index1];
a[index1] = temp;
// Is a[] sorted?
sorted = true;
for (int i = 1; i < a.length; i++) {
if (a[i-1] > a[i]) {
sorted = false;
break;
} // end if
} // end for
} // end while
} // end sort
private int Randomize( int range ) {
double rawResult;
rawResult = Math.random();
return (int) (rawResult * range);
}
} // end BozoSortAlgorithm
To actually run this you'll also need the
SortItem and
SortAlgorithm classes from Sun.
import java.applet.Applet;
import java.awt.*;
import java.util.Vector;
public class JavaDoodle extends Applet {
Vector points = new Vector();
public void paint(Graphics g) {
int x1, y1, x2, y2;
Point tempPoint;
if (points.size() > 1) {
tempPoint = (Point) points.elementAt(0);
x1 = tempPoint.x;
y1 = tempPoint.y;
for (int i = 1; i < points.size(); i++) {
tempPoint = (Point) points.elementAt(i);
x2 = tempPoint.x;
y2 = tempPoint.y;
g.drawLine(x1, y1, x2, y2);
x1 = x2;
y1 = y2;
} // end for
} // end if
}
public boolean mouseDown(Event e, int x, int y) {
points.addElement(new Point(x, y));
return true;
}
public boolean mouseDrag(Event e, int x, int y) {
points.addElement(new Point(x, y));
repaint();
return true;
}
public boolean mouseUp(Event e, int x, int y) {
points.addElement(new Point(x, y));
repaint();
return true;
}
}
import java.applet.Applet;
import java.awt.Event;
import java.awt.Graphics;
public class typewriter extends Applet {
int numcols = 80;
int numrows = 25;
int row = 0;
int col = 0;
char page[][] = new char[numrows][];
public void init() {
for (int i = 0; i < numrows; i++) {
page[i] = new char[numcols];
}
for (int i = 0; i < numrows; i++) {
for (int j = 0; j < numcols; j++) {
page[i][j] = '\0';
}
}
}
public boolean keyDown(Event e, int key) {
char c = (char) key;
switch (key) {
case Event.HOME:
row = 0;
col = 0;
break;
case Event.END:
row = numrows-1;
col = numcols-1;
break;
case Event.UP:
if (row > 0) row--;
break;
case Event.DOWN:
if (row < numrows-1) row++;
break;
case Event.LEFT:
if (col > 0) col--;
else if (col == 0 && row > 0) {
row--;
col=numcols-1;
}
break;
case Event.RIGHT:
if (col < numcols-1) col++;
else if (col == numcols-1 && row < numrows-1) {
row++;
col=0;
}
break;
default:
if (c == '\n' || c == '\r') {
row++;
col = 0;
}
else if (row < numrows) {
if (col >= numcols) {
col = 0;
row++;
}
page[row][col] = c;
col++;
}
else { // row >= numrows
col++;
}
}
repaint();
return true;
}
public void paint(Graphics g) {
for (int i=0; i < numrows; i++) {
String tempString = new String(page[i]);
g.drawString(tempString, 5, 15*(i+1));
}
}
}
As computers grew in power and memory, it was no longer
possible for a programmer to keep track of what was
happening at every location in the machine's physical
memory. Card readers and assembly language were invented to
make programming more feasible. In assembly language the
programmer uses mnemonic codes like MOV to represent
particular bit sequences. These codes mapped directly to
individual instructions on the CPU, and memory was still
addressed directly. One code meant exactly one CPU
instruction.
Assembly language was still a bear to deal with, especially
as related to arrays and storage in memory. Therefore the
first high-level programming language, Fortran, was invented
to spare programmers from the pains of dealing with keeping
track of the location of their variables in memory.
These languages may be compiled or interpreted. In either case your program code needs to be translated into equivalent machine instructions. This level of abstraction made considerably more powerful algorithms and data structures possible.
Java is a very advanced third generation language. Most of the other computer languages you're probably familiar with, Fortran, Basic, C, C++, Cobol, Pascal, as well as most of the one's you're not familiar with (AppleScript, Frontier, Eiffel, Modula-3, ADA, PL/I, etc.) are also third-generation languages (or 3GL's for short).
When third generation languages were invented, they were supposed to make computers so easy to use even the CEO could write programs. This turned out not to be true. Fourth generation languages (or 4GL's for short) moved the abstraction level a step higher. In these languages you tell the computer what results you want rather telling it how to calculate those results. For instance you would ask for the total sales for the year, without specifying the loops necessary to sum all the sales of all the salespeople. SQL is the most popular fourth generation language.
Of all these languages there's no question that 3GL's have been the most successful by almost any measure. A number of different styles of 3GL programming and programming languages have sprung up, most learning from the experience and mistakes of its predecessors. Fortran (and its cousin Basic) were the first. They shared with assembly language an attitude of "Whatever works, no matter how ugly." They had limited flow control (essentially for loops and goto statements) and one data structure, the array. All variables were global and it was impossible to hide one part of the program from any other. Although it was possible to write maintainable, legible code in these languages, few people did.
Pascal and C were the next widely successful languages. They made possible a style of programming known as structured programming. Structured programming languages have many different flow control constructs (switch statements, while loops, and more) as well as tools for more complicated data structures (structs, records and pointers). Goto is deprecated in structured programming though not eliminated entirely. (It is still necessary for some error handling.) Finally they have subroutines with local variables that are capable of splitting the code into more manageable and understandable chunks. These languages proved more capable of writing larger, more maintainable programs. However they too began to bog down when faced with the need to share code among programmers and to write very large (greater than 50,000 line) programs.
Some of the above history may sound a little funny to those of you with experience in the languages I'm discussing. After all Basic has subroutines and local variables, doesn't it? The fact is successful computer languages have continued to evolve. Fortran now has pointers so it can create more complicated data structures. Basic has while loops. Cobol has objects. And on some architectures like Alpha/VMS the assembly language bears little to no resemblance to the underlying machine architecture. These features were not parts of the first versions of the language, however. And despite these improvements the modern versions of these languages are their parents children. Basic and Fortran programmers still often produce spaghetti code. Assembly language is quick to run but long to program. C is obfuscated beyond the comprehension of mere mortals.
The third generation of 3GL's (3.3 GL's) began to take hold
in the late 80's. These were the object oriented languages.
Although object oriented languages had been around since the
late 1960's, it wasn't until the late 80's that computer
hardware became fast enough and memory cheap enough to
support them.
Object oriented languages (OOP for short) included all the features of structured programming and added still more powerful ways to organize algorithms and data structures. There are three key features of OOP languages: encapsulation, polymorphism and inheritance. All of them are tied to the notion of a class.
Programming languages provide a number of simple data types like int, float and String. However very often the data you want to work with may not be simple ints, floats or Strings. Classes let programmers define their own more complicated data types.
For instance let's suppose your program needs to keep a database of web sites. For each site you have a name, a URL, and a description. In traditional programming languages you'd have three different String variables for each web site. With a class you combine these into one package like so:
class website {
String name;
String url;
String description;
}
These variables (name, url and description) are called the
members of the class. They tell you what a class
is and what its properties are. They are the nouns of the
class.
In our web site database we will have many thousands of websites. Each specific web site is an object. The definition of a web site though, which we gave above, is a class. This is a very important distinction. A class defines what an object is, but it is not itself an object. An object is a specific instance of a class. Thus when we create a new object we say we are instantiating the object. Each class exists only once in a program, but there can be many thousands of objects that are instances of that class.
To instantiate an object in Java we use the new
operator. Here's how we'd create a new web site:
website x = new website();
Once we've got a website we want to know something about it.
To get at the member variables of the website we can use
the .
operator. Website has three member
variables, name, url and description, so x has three member
variables as well, x.name, x.url and x.description. We can
use these just like we'd use any other String variables. For
instance:
website x = new website();
x.name = "Cafe Au Lait";
x.url = "http://metalab.unc.edu/javafaq/";
x.description = "Really cool!";
System.out.println(x.name + " at " + x.url + " is " + x.description);
class website {
String name;
String url;
String description;
print() {
System.out.println(name + " at " + url + " is " + description);
}
}
Outside the website method we call the print method just
like we referenced the member variables, using the name of
the particular object we want to print and the
.
operator.
website x = new website();
x.name = "Cafe Au Lait";
x.url = "http://metalab.unc.edu/javafaq/";
x.description = "Really cool!";
x.print();
Notice that within the website class we don't need to use
x.name or x.url. name and url are sufficient. That's
because the print method must be called by a specific
instance of the website class, and this instance knows what
its data is. Or, another way of looking at it, the every
object has its own print method. The print() method is completely enclosed within the website class. All methods in Java must belong to a class. Unlike C++ programs, Java programs cannot have a method hanging around in global space that does everything you forgot to do in your classes.
website x = new website();
website()
is a constructor. If no constructor exists Java provides a
default one, but it's better to make sure you have your own.
You make a constructor by writing a public method that has
the same name as the class. Thus our website constructor is
called website(). Here's a revised website class with a
constructor that initializes all the members to null
Strings.
class website {
String name;
String url;
String description;
public website() {
name = "";
url = "";
description = "";
}
}
Better yet, we should create a constructor that accepts
three Strings as arguments and uses those to initialize the
member variables like so:
class website {
String name;
String url;
String description;
public website(String n, String u, String d) {
name = n;
url = u;
description = d;
}
}
We'd use this like so:
website x = new website("Cafe Au Lait", "http://metalab.unc.edu/javafaq/", "Really cool!");
x.print();
This fits in well with the goal of keeping code relevant to
the proper functioning of a class within the class.
However what if sometimes when we want to create a web site we know the URL, name, and description, and sometimes we don't? Best of all, let's use both!
class website {
String name;
String url;
String description;
public website(String n, String u, String d) {
name = n;
url = u;
description = d;
}
public website() {
name = "";
url = "";
description = "";
}
}
This is called method overloading or
polymorphism. Polymorphism is a feature of object
oriented languages that lets one name refer to different
methods depending on context. The important context is
typically the number and type of arguments to the method.
In this case we use the first version of the method if we
have three String arguments and the second version if we
don't have any arguments.
If you have one or two or four String arguments to the constructor, or arguments that aren't Strings, then the compiler generates an error because it doesn't have a method whose signature matches the requested method call.
System.out.println()
to print any object. However for good
results your class should have a toString()
method that
formats the objects data in a sensible way and returns a
String. Here's how we'd implement it in the website
example:
public class ClassTest {
public static void main(String args[]) {
website x = new website("Cafe Au Lait", "http://metalab.unc.edu/javafaq/", "Really cool!");
System.out.println(x);
}
}
class website {
String name;
String url;
String description;
public website(String n, String u, String d) {
name = n;
url = u;
description = d;
}
public website() {
name = "";
url = "";
description = "";
}
public String toString() {
return (name + " at " + url + " is " + description);
}
}
Java is the latest and possibly the greatest third generation programming language. Here I need to explain why Java is a better OOP language than C++.
public class ComplexNumber extends Object {
public double u;
public double v;
}
While this is sufficient to encompass all the data that one needs in
a complex number it's not a very good example of object-oriented programming.
To actually do anything with this number we have to know exactly how the
data structure is defined. If we change the data structure, for instance by
defining a complex number in terms of it's magnitude r and its argument theta
instead of by its real and imaginary components
we have to change all the code that depends on it.We also have to write code to explicitly add the numbers, multiply them or do anything else we might need to do with complex numbers. If we need to add complex numbers in more than one place, then we need to write the addition code again, or, at the very least, copy and paste it.
A better implementation of a complex number class will shield us from the exact storage of the data, i.e. x and y vs. r and theta. It will also provide methods that let us perform any operation we might need to perform on or with a complex number.
Before writing code we need to ask ourselves what we'll do with a complex number. Most objects first require a constructor, a method that is called when you create a new complex number. A more complicated object may also require a destructor method that's called when you get rid of an object; but since this is a fairly simple object, we'll let Java's built-in garbage collection take care of that for us.
Since these are complex numbers it's not unlikely that we'll need to add them, subtract them, multiply them and divide them. We'll also want to be able to access their real and imaginary parts as well as their absolute values and arguments. The following class does all that.
//
public class Complex extends Object {
private double u;
private double v;
Complex (double x, double y) {
u=x;
v=y;
}
public double Real () {
return u;
}
public double Imaginary () {
return v;
}
public double Magnitude () {
return Math.sqrt(u*u + v*v);
}
public double Arg () {
return Math.atan2(v, u);
}
// Add z to w; i.e. w += z
public Complex Plus (Complex z) {
return new Complex(u + z.u, v + z.v);
}
// Subtract z from w
public Complex Minus (Complex z) {
return new Complex(u - z.u, v - z.v);
}
public Complex Times (Complex z) {
return new Complex(u*z.u - v*z.v, u*z.v + v*z.u);
}
// divide w by z
public Complex DivideBy (Complex z) {
double rz = z.Magnitude();
return new Complex((u * z.u + v * z.v)/(rz*rz),(v * z.u - u * z.v)/(rz*rz));
}
}
Notice especially that u
and v
are now private. They cannot be accessed
by external code even if we want them to be.
The use of one of these
methods will look like the following. Add the following ComplexExamples
class to the Complex.java file and compile. Then run ComplexExamples
in the
usual way by typing java ComplexExamples.
//Complex Arithmetic Examples
class ComplexExamples {
public static void main (String args[]) {
Complex u, v, w, z;
u = new Complex(1,2);
System.out.println("u: " + u.Real() + " + " + u.Imaginary() + "i");
v = new Complex(3,-4.5);
System.out.println("v: " + v.Real() + " + " + v.Imaginary() + "i");
// Add u + v;
z=u.Plus(v);
System.out.println("u + v: "+ z.Real() + " + " + z.Imaginary() + "i");
// Add v + u;
z=v.Plus(u);
System.out.println("v + u: "+ z.Real() + " + " + z.Imaginary() + "i");
z=u.Minus(v);
System.out.println("u - v: "+ z.Real() + " + " + z.Imaginary() + "i");
z=v.Minus(u);
System.out.println("v - u: "+ z.Real() + " + " + z.Imaginary() + "i");
z=u.Times(v);
System.out.println("u * v: "+ z.Real() + " + " + z.Imaginary() + "i");
z=v.Times(u);
System.out.println("v * u: "+ z.Real() + " + " + z.Imaginary() + "i");
z=u.DivideBy(v);
System.out.println("u / v: "+ z.Real() + " + " + z.Imaginary() + "i");
z=v.DivideBy(u);
System.out.println("v / u: "+ z.Real() + " + " + z.Imaginary() + "i");
}
}
z = u.Add(u);
How about if we multiply, divide or subtract? e.g.
z = u.Multiply(u);
z = u.Divide(u);
z = u.Minus(u);
System.out.println(u);
instead? It turns out we can. All objects have a toString method which is inherited from the Object class. However the default toString() method isn't very useful so we want to override it with one of our own creation that handles the conversion to complex numbers. Add the following method to the Complex class:
public String toString() {
if (v >= 0) return (String.valueOf(u) + " + " + String.valueOf(v) + "i");
else return (String.valueOf(u) + " - " + String.valueOf(-v) + "i");
}
You should also modify the ComplexExamples class as follows:
class ComplexExamples {
public static void main (String args[]) {
Complex u, v, z;
u = new Complex(1,2);
System.out.println("u: " + u);
v = new Complex(3,-4.5);
System.out.println("v: " + v);
// Add u + v;
z=u.Plus(v);
System.out.println("u + v: " + z);
// Add v + u;
z=v.Plus(u);
System.out.println("v + u: " + z);
z=u.Minus(v);
System.out.println("u - v: " + z);
z=v.Minus(u);
System.out.println("v - u: " + z);
z=u.Times(v);
System.out.println("u * v: " + z);
z=v.Times(u);
System.out.println("v * u: "+ z);
z=u.DivideBy(v);
System.out.println("u / v: " + z);
z=v.DivideBy(u);
System.out.println("v / u: " + z);
}
}
That's about an order of magnitude easier to understand and to write.
public Complex Times (double x) {
return new Complex(u*x, v*x);
}
Here's a simple test program for your new method:
class RealComplex {
public static void main (String args[]) {
Complex v, z;
double x = 5.1;
v = new Complex(3,-4.5);
System.out.println("v: " + v);
System.out.println("x: " + x);
z=v.Times(x);
System.out.println("v * x: " + z);
}
}
The astute among you may be saying to hold on here, we've redefined the
Times method. Now how can we multiply two complex numbers?
However there's really no problem. The compiler notices
that the arguments of the two methods named Times (not the same as the
arguments of the two complex numbers but unfortunately the terminology
fails us here) are different. One multiplies two complex numbers, the other
multiplies a real number and a complex number. The compiler is smart enough
to figure out which version of Times to use when. This is called
method overloading or polymorphism.
In some object-oriented languages like C++ you can not only overload methods but even operators like + and =. However while this makes numeric classes like complex numbers easier to work with, it tends to lead to unreadable and unmaintainable code for non-numeric classes. Therefore Java's designers elected not to add this feature to the language. As you can see from our example, with a little forethought you really don't lose very much without operator overloading.
There has been some code that hasn't resided in our source files. Remember all those import statements at the top of every file? What they do is pull in prewritten and precompiled code from various locations so we can use it in our programs. You can do the same thing with classes you write. However to do this you do need to be aware of several conventions and restrictions.
Next save the examples from the previous exercises in a separate file called ComplexExamples.java in the same directory as Complex.java. Now compile both files and run Complex Examples.java.
Here and in the future we are going to try to separate the mathematical definition of our data from its display on the screen. In fact we won't even add the screen display till the second iteration of the program.
Our data structure will be a two dimensional array, each element of which represents a fixed point in the complex plane. The array is defined by the value of the lower left point, the gap between points and the number of pixels in the x and y directions. Thus given that element (0,0) of the array matches the complex point (x0,y0), each point is separated by a value gap, we know that element i,j of the array represents the point (x0 + i*gap, y0 + j*gap) in the complex plane. Since we know this by position of the array element alone we dont' need to store this value in the array.
What will we store in each element of this array? We'll calculate a number to go there in the following fashion. Let z = 0 + 0i and let c be the position of that array element in complex space. calculate z = z*z + c and iterate as follows up to 200 times:
for (i=0; i < 200; i++) {
z = z.Times(z) + c;
}
The Mandelbrot set is composed of those elements which, no matter
how long we do this, never approach infinity. Since infinity can take
a mighty long time to approach, it's fortunate that a fairly elementary theorem
in complex variable theory guarantees that any number whose magnitude
ever exceeds two in this iterative scheme will become as large
as one might wish. (i.e. They asymptotically approach infinity.)
Therefore once a number exceeds two we can break out of the loop and
say definitively that this number is not in the Mandelbrot set.
Unfortunately there's no guarantee that just because an element doesn't reach 2 in two hundred iterations it might not reach two on the two hundredth and first iteration or the two thousandth or the two millionth. However most numbers that don't prove they're not in the Mandelbrot Set by the two hundredth iteration are reasonably likely to be in it.
Here's how the code will work. First we'll select the lower left hand corner of our rectangle in complex space, the size of the gap between the points and the number of points in each dimension. For a specific example we can choose the square bordered on the lower left by (-2,-2) and on the upper right by (2,2). To keep initial computations manageable we'll break this up into an array of 101 by 101 elements which implies a gap size of 0.05.
Once this array is created we'll loop through it and fill each element with a Boolean value, true if the element is probably in the Mandelbrot Set (doesn't pass two in two hundred iterations) and false if it's not (does pass two and thus go to infinity). Here's the code:
class MandelApp {
public static void main(String args[]) {
int xdim = 101;
int ydim = 101;
double xstart = -2.0;
double ystart = -2.0;
boolean Mandel[][] = new boolean[xdim][ydim];
double gap = 0.05;
int max_iterations = 200;
int i,j,k;
Complex z, c;
for (i=0; i < xdim; i++) {
for (j=0; j < ydim; j++) {
c = new Complex(xstart + i*gap ,ystart + j*gap);
z = new Complex(0.0, 0.0);
k=0;
while (z.Magnitude() < 2.0 && k < max_iterations) {
z = z.Times(z);
z = z.Plus(c);
k++;
}
if (z.Magnitude() < 2.0) {
Mandel[i][j] = true;
}
else Mandel[i][j] = false;
}
}
}
}
import java.applet.Applet;
import java.awt.*;
public class Mandelbrot extends Applet {
int xdim;
int ydim;
double xstart = -2.0;
double ystart = -1.25;
int Mandel[][];
double gap = 0.05;
int max_iterations = 256;
public void paint(Graphics g) {
int i,j,k;
Complex z, c;
xdim = size().width;
ydim = size().height;
gap = 2.5/ydim;
Mandel = new int[xdim][ydim];
for (i=0; i < xdim; i++) {
for (j=0; j < ydim; j++) {
c = new Complex(xstart + i*gap, ystart + j*gap);
z = new Complex(0.0, 0.0);
for (k = 0; z.Magnitude() < 2.0 && k < max_iterations; k++) {
z = z.Times(z);
z = z.Plus(c);
}
g.setColor(selectColor(k));
g.fillRect(i, j, 1, 1);
}
}
}
protected Color selectColor (int num_iterations) {
if (num_iterations > max_iterations) return Color.black;
else if (num_iterations > 9*max_iterations/10) return Color.darkGray;
else if (num_iterations > 8*max_iterations/10) return Color.gray;
else if (num_iterations > 7*max_iterations/10) return Color.magenta;
else if (num_iterations > 6*max_iterations/10) return Color.cyan;
else if (num_iterations > 5*max_iterations/10) return Color.blue;
else if (num_iterations > 4*max_iterations/10) return Color.green;
else if (num_iterations > 3*max_iterations/10) return Color.yellow;
else if (num_iterations > 2*max_iterations/10) return Color.orange;
else if (num_iterations > 1*max_iterations/10) return Color.red;
else return Color.white;
}
}
This program is minimal. It should really create an ImageProducer
which draws the
Mandelbrot set. There are also a lot of additions that could be made to the parameters
to allow for zooming in on particular regions. In fact you could even implement this
as a Canvas
in an applet with various controls to select the area of interest.
This will all be investigated in the Cafe Au Lait newsletter.
For more details on the Mandelbrot Set see the first chapter
of
The presentations at the first Java Day in New York City
were crucial to getting my understanding of Java off the
ground, especially those of
The eventtutor applet, flying lines applet and Mondrian
applets are taken in spirit if not in code from
Finally I'd like to thank
Alden Hosting offers private JVM (Java Virtual Machine), Java Server Pages (JSP), Servlets, and Servlets Manager with our Web Hosting Plans WEB 4 PLAN and WEB 5 PLAN , WEB 6 PLAN .
At Alden Hosting we eat and breathe Java! We are the industry leader in providing affordable, quality and efficient Java web hosting in the shared hosting marketplace. All our sites run on our Java hosing platform configured for optimum performance using Java, Tomcat, MySQL, Apache and web application frameworks such as Struts, Hibernate, Cocoon, Ant, etc.
We offer only one type of Java hosting - Private Tomcat. Hosting accounts on the Private Tomcat environment get their very own Tomcat server. You can start and re-start your entire Tomcat server yourself.
![]() |
|
http://alden-servlet-Hosting.com
JSP at alden-servlet-Hosting.com
Servlets at alden-servlet-Hosting.com
Servlet at alden-servlet-Hosting.com
Tomcat at alden-servlet-Hosting.com
MySQL at alden-servlet-Hosting.com
Java at alden-servlet-Hosting.com
sFTP at alden-servlet-Hosting.com
http://alden-tomcat-Hosting.com
JSP at alden-tomcat-Hosting.com
Servlets at alden-tomcat-Hosting.com
Servlet at alden-tomcat-Hosting.com
Tomcat at alden-tomcat-Hosting.com
MySQL at alden-tomcat-Hosting.com
Java at alden-tomcat-Hosting.com
sFTP at alden-tomcat-Hosting.com
http://alden-sftp-Hosting.com
JSP at alden-sftp-Hosting.com
Servlets at alden-sftp-Hosting.com
Servlet at alden-sftp-Hosting.com
Tomcat at alden-sftp-Hosting.com
MySQL at alden-sftp-Hosting.com
Java at alden-sftp-Hosting.com
sFTP at alden-sftp-Hosting.com
http://alden-jsp-Hosting.com
JSP at alden-jsp-Hosting.com
Servlets at alden-jsp-Hosting.com
Servlet at alden-jsp-Hosting.com
Tomcat at alden-jsp-Hosting.com
MySQL at alden-jsp-Hosting.com
Java at alden-jsp-Hosting.com
sFTP at alden-jsp-Hosting.com
http://alden-java-Hosting.com
JSp at alden-java-Hosting.com
Servlets at alden-java-Hosting.com
Servlet at alden-java-Hosting.com
Tomcat at alden-java-Hosting.com
MySQL at alden-java-Hosting.com
Java at alden-java-Hosting.com
sFTP at alden-java-Hosting.com
JSP
Servlets
Tomcat
mysql
Java
JSP
Servlets
Tomcat
mysql
Java
JSP
Servlets
Tomcat
mysql
Java
JSP
Servlets
Tomcat
mysql
Java
JSP at JSP.aldenWEBhosting.com
Servlets at servlets.aldenWEBhosting.com
Tomcat at Tomcat.aldenWEBhosting.com
mysql at mysql.aldenWEBhosting.com
Java at Java.aldenWEBhosting.com
Web Hosts Portal
Web Links
Web Links JSP
Web Links servlet
Tomcat Docs
Web Links
Web Links JSP
Web Links servlet
Web Hosting
Tomcat Docs
JSP Solutions Web Links
JSP Solutions Web Hosting
Servlets Solutions Web Links
Servlets Solutions Web Hosting
Web Links
Web Links
.
.
.
.
.
.
.
.
.
.
jsp hosting
servlets hosting
web hosting
web sites designed
cheap web hosting
web site hosting
myspace web hosting