Java Notes
by C.A. Bertulani - e-mail: bertu@if.ufrj.br, URL: http://www.if.ufrj.br/persons/bertulani.html

These notes are intended to give an introduction to the Java programming language. They have been adapted and extended from several sources (see credits at the end of these notes). The material can be easily covered in 3 - 4 days for the reader with some experience in programming. At the end of that the reader will be able to write and run its own applets.

Table of Contents

I - Introduction

These notes assume that you have the Java Development Kit (JDK) version 1.1 or later from Sun (http://www.javasoft.com) and a simple text editor such as NotePad.  The instructions below will help you with that.

 I.1 - Installing Java

  • The Java compiler is available from Sun for Windows  and Windows NT, for UNIX,  and MacIntoshes.
  • The basic Java environment consists of a web browser that can play Java applets, a Java compiler to turn to Java source code into byte code, and a Java interpreter to run Java programs. These are the three key components of a Java environment. You'll also need a text editor like NotePad.
  • Sun has made the Java Developers Kit available for its supported platforms. It includes an applet viewer that will let you view and test your applets. The JDK also includes the javac compiler, the java interpreter, the javaprof profiler, the javah header file generator (for integrating C into your Java code), the Java debugger and limited documentation. However most of the documentation for the API and the class library is on Sun's web site.
  • Windows Installation Instructions

  • The Windows release is a self extracting archive. You will need about 50 megabytes of free disk space to install the JDK. Execute the file by double-clicking on it in the File Manager or by selecting Run... from the Program Manager's File menu and typing the path to the file. This will unpack the archive. The full path is unimportant. It could be your C: drive. If this is the case the files will live in C:\java. If you unpacked it somewhere else just replace C:\ by the full path to the java directory in what follows. You will need to add C:\java\bin directory to your PATH environment variable
  • Unix Installation Instructions

  • If you're on a shared system at a university or an Internet service provider, there's a good chance Java is already installed. Ask your local support staff how to access it. Otherwise follow these instructions.
    The Unix release is a compressed tar file. You will need about 80 megabytes of disk space to uncompress and untar the JDK. You do this with the commands:
    % uncompress JDK-1_0_2-solaris2-sparc.tar.Z
    % tar xvf JDK-1_0_2-solaris2-sparc.tar
    The exact file name may be a little different if you're retrieving the release for a different platform such as Irix or if the version is different. You can untar it in your home directory, or, if you have root privileges, in some convenient place like /usr/local where all users can have access to the files. However root privileges are not necessary to install or run Java. Untarring the file creates all necessary directories and sub-directories. The exact path is unimportant. It could be in /usr/local. If a sysop already installed it, this is probably where it lives. (Under Solaris it's also possible the sysop put it into /opt.) If this is the case the files live in /usr/local/java. If you unpacked it somewhere else, just replace /usr/local by the full path to the java directory in what follows. If you installed it in your home directory, you can use ~/java and ~/hotjava instead of a full path.
    You now need to add /usr/local/java/bin directory to your PATH environment variable. You use one of the following commands depending on your shell.
    csh, tcsh:
     % set path=($PATH /usr/local/java/bin)
    sh:
     % PATH=($PATH /usr/local/java/bin); export $PATH
    You should also add these lines to the end of your .profile and .cshrc files so you won't have to do this every time you login.

     I.2 - What is Java?

  • Java is a "simple, object-oriented, distributed, interpreted, robust, secure, architecture neutral, portable, high-performance, multithreaded, and dynamic language."
  • Java was made to look and act much like C and C++, since so many people are already familiar with those languages. However, to simplify matters, several features common to C and C++ were not included. Among those are pointers, header files, and goto's. Java is not 100% object-oriented like SmallTalk, but it is much more so that C++.
  • Distributed: Java was designed with today's distributed networks in mind. It is capable of network communication and handling socket calls.
  • Interpreted: Java code is compiled into byte-code, which is somewhat similar to a binary executable. Instead of being run by your operating system, though, the byte-code is run by the Java Virtual Machine, a byte-code interpreter. The advantage is that the Java code doesn't have to be directly translated into the system-specific machine code. Instead, the Java Virtual Machine acts as an intermediary which will be the same on every system. So, the end result is code that acts the same on every computer.
  • Robust: Java is a strongly typed language, and so will catch most errors long before run-time. Also, since Java doesn't support pointers, code written in Java is much more reliable. Another feature adding to Java's robustness is its exception handling. When an error occurs, the code is able to deal with it on the fly, allowing for a high rate of recovery.
  • Secure: Java was designed with the internet in mind. As such, it has to be secure to be usable at all. Java applets are not allowed to do any file I/O. Also, the lack of pointers prevents a large amount of potential problems. The internet is a dangerous place. There is really no way to be completely secure without being useless. Java has tried to find a middle ground between being unprotected and being unusable.
  • Portable: Because Java runs on the Java Virtual Machine, and not the individual system itself, Java code is easily portable to any system capable of running the virtual machine.
  • Performance: Java is approximately 20 times slower than C. However, it is more than fast enough for most of its more commons uses such as applets and multimedia. Java does allow you, though, to import native C code for the times when speed is essential.
  • Multithreaded: Java can run several simultaneous process threads. This allows the user to easily code programs with can do more than one thing at once. Such multitasking capability is invaluable for use in building GUI's (Graphic Unit Interface) and other such applications.
  • Dynamic: Java was built to be able to adapt to the continually evolving environment. It can dynamically load in classes when they are needed, and it can even got out over the 'Net to find them.
  • Initially computer memories were so small and the machines so slow, that program efficiency was the primary concern.  Algorithms were very closely tied to the capabilities of the specific machine they ran on. This is called machine language programming. The toggling of individual memory locations (by switch or other means) is called a first-generation language. In a first generation language there is almost no abstraction.
  • 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.
  • 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.  Fortran was the first example of a third-generation language. In a third generation language you tell the computer the algorithms and data structures it should use to calculate the results you want; but you use more abstract logical and mathematical operators rather than directly manipulating addresses in memory and CPU instructions. In a third generation language, statements represent several machine instructions. Which instructions they represent may even depend on their context. 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). 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.
  • 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).  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.
  • 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 programming is not a panacea. It adds a speed penalty over C or Fortran code, and often requires twice as much memory.)
  •  I.3 - Object-Oriented Programming

  • Object-Oriented Programming, or OOP, is a method of coding which results in better, more reliable and more reusable code.
  • Objects. "The fundamental idea behind object-oriented languages is to combine into a single unit both data and the functions that operate on that data. Such a unit is called an object."
  • Code in OOP. Let's say we have to write code that will model a store. We have to keep track of customers, how much money the customers have, and how much the customers have bought. We have to keep track of the sales clerks, where they are and when their shifts are. And we have to keep track of the merchandise, how much we have and where it is.

  • First we'll look at a Structural method. To keep track of the customers and their money and purchases, we'll need an array of structures. Same for the clerks, and same for the merchandise. That pretty much takes care of the data.
    Now, we'll need a function to spend a customer's money, one to add a customer's purchases, one to move the clerks, one to update the clerks' shifts, one to move merchandise, and one to sell the merchandise. Each of these functions will be globally available and will be called by a larger control structure. Also, all of the data will be visible to all of the functions.
    Now, let's look at an OOP implementation. We'll define three objects, Customer, Clerk, and Merchandise. The Customer object will have a Money variable and a Purchases array. The Customer object will also have a Spend method and a Buy method to update the money and purchases, respectively.
    The Clerk object will have a Location variable and Shift variable. It will have a Move method and a CheckShift method.
    The Merchandise object will have a Price variable and a Location variable. It will have Move and Sell methods.
    We will have to have three arrays, one for each object type. The methods will be visible to the control structure, but the data will be hidden, or encapsulated, within each object.
    So, what's the difference? They both have pretty much the same stuff; it's just arranged a little differently.
    Well, the different arrangement is what makes OOP more powerful. The first obvious advantage is that the data is hidden. You cannot accidentally alter the data for a customer while fiddling with something else. You can only access one of the object's data by directly going through that object.
    Another advantage is reusability of the code. Say, for instance, we now have to simulate an assembly line. One of the things we'll need to keep track of is where the workers are, and who's currently working. We can just borrow the Clerk object from our current model and plop it in our new project without any modification! It already knows how to keep track of where it is, and when it's shifts begin and end. That's all we need for an assembly-line worker.
    Try doing that with the Structured code! You'd have to copy out the code for the structure, the code for each of the relevant functions, and modify the functions to fit the new array name, along with some other house keeping.
    OOP allows you to bundle together related data and functions so that they can be easily moved around and reused.
    Yet another advantage is in maintenance. Say we want to be more specific now. We want to model a grocery store. And, we don't want to keep track of just "merchandise." We want to keep track of meat, fruits and vegetables, and dry goods.
    With the structured method, we'd have to trade the merchandise array for three separate item arrays. We would then have to write three new structures to handle the different data to be kept for each item. We would also have to modify all the functions which used the merchandise array to now work with the three new arrays, which could be a pain by itself!
  • Inheritance. With OOP we could take advantage of a property called inheritance.

  • Inheritance allows an object to inherit code from another object, saving the time of writing all new code.
    We could create three new objects, Meat, Fruit_N_Veggies and Dry_Goods. Each of these new objects can be a child of our previous Merchandise class. So, each will automatically inherit all of the data and function contained in Merchandise.
    Then, we could further specify each new object. For example, for Meat, we could add a SellDate variable and a Spoiled method so that we can measure losses from spoilage. Now, here's the really cool part. Since they are all children of Merchandise, they can all still go in the Merchandise array. We don't have to make a new array for each of them.
  • Polymorphism. When we create a new Clerk, we want to be able to initialize his or her starting position and shifts. So, when we create a Clerk, we can do this:

  • Clerk new_clerk = new Clerk();
    new_clerk.Location = "aisle 9";
    new_clerk.Shift = "12pm-12am";
    But, this is rather long-winded and clumsy. We can define something called a constructor, which we'll discuss later, which will allow us to do the same thing like this:
    Clerk new_clerk = new Clerk("aisle 9", "12pm-12am");
    Now, isn't that better? But, the code for initializing the object is in the constructor, so we haven't really changed anything.
    Here is where the power of polymorphism comes in. What if we don't want to always assign both the shift and the location at the same time? What if we want to be able to just assign the location and leave the shift until later? We can do that without any problem. We can define another constructor so that we can do this:
    Clerk new_clerk = new Clerk("aisle 9");
    Now, look at the two initializations by constructor. They both call Clerk() (which is the name of the constructor). How can they both be named the same, but use different arguments?
    The answer is polymorphism! In OOP you can declare a method or constructor more than once, each with a different set of arguments, but the same name. When you call the method, the compiler knows from the argument list, called the signature, which version of the method you mean.
    This may not be a major code-saving feature, but it reduces confusion by 100%. Instead of add_int(), add_float(), add_short(), etc., you can just have an add() which will know by the arguments which one you need.

    1. Main Features of the Java Language

    1.1 - Primitive Data Types

  • In Java Integer numeric types are 8-bit byte, 16-bit short, 32-bit int, and 64-bit long. A byte is a 8 binary digit. The representation of decimal numbers increases by a factor of 2 with each increasing bit. Thus, a n-bit type can hold (2n -1) decimal numbers. Thus a byte can hold decimals from -128 to 127. A long is an integer type variable that can hold up to 9,223,372,036,854,775,807.
  • A Float is a single-precision floating point numeric type with a 32-bit IEEE size (IEEE - Institute of Electrical and Electronics Engineers, New York, www.ieee.org).  To represent a real number in binaries, part of the binaries are use for the mantissa (significant digits) and the other part for the exponent of 10. The largest number a float can hold is 3.4028235E38.
  • A Double is double-precision floating point. The largest double value in Java is 1.7976931348623157E308.

  • Character data types are 16-bit byte, running from 0 through 65,535.
    A Java boolean variable assumes the value true or false. A Java boolean type can't  be converted to any numeric type.

     1.1.1 - Declaration and initialization of data types

  • One declares a data type as follows

  • int Integername;
    short IntegerShort;
    char ThisChar;
    . . . . .
  • After declaration, one initializes a data type as

  • IntegerName = 125;
    IntegerShort = 32;
    ThisChar = 'Q';   // Characters are defined within single quotes
  • Once can also declare and initialize the data types simultaneously.  The program below declares, initializes,  and prints out the largest numbers hold by the data types in Java.
  • Compile and Run it. Copy this program to your java work directory, compile it (in the DOS window type: javac MaxVariablesDemo.java and run it by typing javaMaxVariablesDemo). For the moment, don't worry much about the verbosity occurring in the first 2 lines of the code and on the printout lines. But, notice that from some name definitions (syntax) it is quite straightforward to understand what the program wants to do. To make an output to a file type java MaxVariablesDemo > OutputFileName.out To be able to compile and run from your directory, you have to set the PATH to it. Follow the instructions from the JDK installation to find out how change your PATH environment variable.

  • public class MaxVariablesDemo {   // defines the class
        public static void main(String args[]) { // the main method inside the class
            // integers
            byte largestByte = Byte.MAX_VALUE;  // MAX_VALUE is of course the maximum value
            short largestShort = Short.MAX_VALUE;
            int largestInteger = Integer.MAX_VALUE;
            long largestLong = Long.MAX_VALUE;
            // real numbers
            float largestFloat = Float.MAX_VALUE;
            double largestDouble = Double.MAX_VALUE;
            // other primitive types
            char aChar = 'S';
            boolean aBoolean = true;
            // display them all
            System.out.println("The largest byte value is " + largestByte);
            System.out.println("The largest short value is " + largestShort);
            System.out.println("The largest integer value is " + largestInteger);
            System.out.println("The largest long value is " + largestLong);
            System.out.println("The largest float value is " + largestFloat);
            System.out.println("The largest double value is " + largestDouble);
            if (Character.isUpperCase(aChar)) {
                System.out.println("The character " + aChar + " is upper case.");
            } else {
                System.out.println("The character " + aChar + " is lower case.");
            }
            System.out.println("The value of aBoolean is " + aBoolean);
        }
    }
    Its output yields:
    The largest byte value is 127
    The largest short value is 32767
    The largest integer value is 2147483647
    The largest long value is 9223372036854775807
    The largest float value is 3.4028235E38
    The largest double value is 1.7976931348623157E308
    The character S is upper case.
    The value of aBoolean is true
  • The Java language has some predefined libraries, or packages. This program only needs the standard library. When we type Byte.MAX_VALUE we access the maximum value of the object Byte, defined in this package. System.out.println is a function (in Java we say Method) contained in this package, which prints out everything written inside double quotes. The numeric value of the variable is written after the quotation by concatenating its name with a plus sign. Finally, the method Character.isUpperCase() checks if the variable aChar is uppercase or not. You will see several methods like this along these notes. They are all inherited from the Java packages and their action are most of times easy to figure out from their names. Try to memorize their name definitions.
  • 1.2 Blocks and Scope

  • Blocks are important both syntactically and logically. Without the braces the code wouldn't compile. The compiler would have trouble figuring out where one method or class ended and the next one began. Similarly it would be very difficult for someone else reading your code to understand what was going on. For that matter it would be very difficult for you, yourself to understand what was going on. The braces, or curly brackets { }, are used to group related statements together. In the broadest sense everything between matching braces is executed as one statement (though depending not necessarily everything inside the braces is executed every time).
  • Blocks can be hierarchical. One block can contain one or more subsidiary blocks. In this case we have one outer block that defines the MaxVariablesDemo class. Within the MaxVariablesDemo block we have a method block called main.
  • A variable's scope is the block of code within which the variable is accessible and determines when the variable is created and destroyed.  The variable inside the block is a local variable.
  • You can declare local variables anywhere in a method or within a block of code in a method. In MaxVariablesDemo, all of the variables declared within the main method are local variables. The scope of each variable -- the code that can access each variable -- extends from the declaration of the variable to the end of the main method (indicated by the first right curly bracket } that appears in the program code). As soon as the variable goes out of scope, it does not exist and is a candidate for the garbage collector. The garbage collector is a part of the Java interpreter that deletes from memory all unused objects. Ex:

  • {
    some code here  // no x defined here
    {
    more code here
    int x  = 2;
    }
    y = x;  // WRONG. No x defined in this block
    int x = 3;  // OK. This x is different from the one in the above block
    more code here
    }

    1.3 - Comments

  • Comments can appear anywhere in a source file. Comments are identical to those in C and C++. Everything between /* and */ , or after //, is ignored by the compiler and everything on a line after two consecutive slashes is also thrown away. Therefore the following program is, as far as the compiler is concerned, identical to the first one:

  • // This is the Hello World program in Java
    class HelloWorld {
        public static void main (String args[]) {
          /* Now let's print the line Hello World */
          System.out.println("Hello World");
      }
    }
    which yields
    Hello World

    1.4 - Operators

  • An operator performs a function on either one, two, or three operands. An operator that requires one operand is called a unary operator. For example, ++ is a unary operator that increments the value of its operand by 1. An operator that requires two operands is a binary operator. For example, = is a binary operator that assigns the value from its right-hand operand to its left-hand operand. And finally a ternary operator is one that requires three operands. The Java programming language has one ternary operator, ?:, which is a short-hand if-else statement.
  •  1.4.1 - Arithmetic Operators

    Operator Use Description
    + op1 + op2 Adds op1 and op2
    - op1 - op2  Subtracts op2 from op1
    * op1 * op2 Multiplies op1 by op2
    / op1 / op2 Divides op1 by op2
    % op1 % op2 Computes the remainder of dividing op1 by op2
    Ex:
    int i = 37;
    int j = 42;
    double x = 27.475;
    double y = 7.22;
    System.out.println("    i % j = " + (i % j)); // prints  i % j = 37
    System.out.println("    x % y = " + (x % y)); // prints x % y = 5.815
    Operator Use Description
    ++ op++ Increments op by 1; evaluates to the value of op before it was incremented 
    ++ ++op Increments op by 1; evaluates to the value of op after it was incremented 
    -- op-- Decrements op by 1; evaluates to the value of op before it was decremented 
    -- --op Decrements op by 1; evaluates to the value of op after it was decremented 
    Ex: Copy this program to a file named AutoInc.java and run it to understand the action of post- (pre-) (in) decrement operators.
    // Demonstrates the ++ and -- operators.
    public class AutoInc {
      public static void main(String[] args) {
        int i = 1;
        prt("i : " + i);
        prt("++i : " + ++i); // Pre-increment
        prt("i++ : " + i++); // Post-increment
        prt("i : " + i);
        prt("--i : " + --i); // Pre-decrement
        prt("i-- : " + i--); // Post-decrement
        prt("i : " + i);
      }
      static void prt(String s) {  // to save typing
        System.out.println(s);
      }
    }
    which yields
    i : 1
    ++i : 2
    i++ : 2
    i : 3
    --i : 2
    i-- : 2
    i : 1

     1.4.2 - Relational and logical operators

    Operator Use Returns true if
    > op1 > op2  op1 is greater than op2
    >= op1 >= op2 op1 is greater than or equal to op2
    < op1 < op2 op1 is less than op2
    <= op1 <= op2  op1 is less than or equal to op2
    == op1 == op2 op1 and op2 are equal
    != op1 != op2  op1 and op2 are not equal 
    Operator Use Returns true if
    && op1 && op2 op1 and op2 are both true, conditionally evaluates op2
    || op1 || op2 either op1 or op2 is true, conditionally evaluates op2
    ! ! op op is false
    & op1 & op2 op1 and op2 are both true, always evaluates op1 and op2
    | op1 | op2 either op1 or op2 is true, always evaluates op1 and op2
    ^ op1 ^ op2 if op1 and op2 are different--that is if one or the other of the operands is true but not both 
    Ex: The following  code test the use of relational and logical operators.
    // Relational and logical operators.
    import java.util.*;
    public class Bool {
      public static void main(String[] args) {
        Random rand = new Random();  // Java random number generator
        int i = rand.nextInt() % 100;
        int j = rand.nextInt() % 100;
        prt("i = " + i);
        prt("j = " + j);
        prt("i > j is " + (i > j));
        prt("i < j is " + (i < j));
        prt("i >= j is " + (i >= j));
        prt("i <= j is " + (i <= j));
        prt("i == j is " + (i == j));
        prt("i != j is " + (i != j));
        // Treating an int as a boolean is
        // not legal Java
    //! prt("i && j is " + (i && j));
    //! prt("i || j is " + (i || j));
    //! prt("!i is " + !i);
        prt("(i < 10) && (j < 10) is "
           + ((i < 10) && (j < 10)) );
        prt("(i < 10) || (j < 10) is "
           + ((i < 10) || (j < 10)) );
      }
      static void prt(String s) {   // to save typing
        System.out.println(s);
      }
    }
    In one run of this code I've got
    i = -49
    j = 29
    i > j is false
    i < j is true
    i >= j is false
    i <= j is true
    i == j is false
    i != j is true
    (i < 10) && (j < 10) is false
    (i < 10) || (j < 10) is true

     1.4.3 - Bitwise Operators

    Operator Use Operation
    >> op1 >> op2 shift bits of op1 right by distance op2
    << op1 << op2 shift bits of op1 left by distance op2
    >>> op1 >>> op2 shift bits of op1 right by distance op2 (unsigned) 
    Ex: Each shift operator shifts the bits of the left-hand operand over by the number of positions indicated by the right-hand operand. The shift occurs in the direction indicated by the operator itself. For example, the following statement shifts the bits of the integer 13 to the right by one position:
            13 >> 1;
    The binary representation of the number 13 is 1101. The result of the shift operation is 1101 shifted to the right by one position -- 110 or 6 in decimal.
  • If you shift a char, byte, or short, it will be promoted to int before the shift takes place, and the result will be an int. Only the five low-order bits of the right-hand side will be used. This prevents you from shifting more than the number of bits in an int. If you're operating on a long, you'll get a long result. Only the six low-order bits of the right-hand side will be used so you can't shift more than the number of bits in a long.
  • Operator Use Operation
    & op1 & op2 bitwise and
    | op1 | op2 bitwise or
    ^ op1 ^ op2 bitwise xor
    ~ ~op2 bitwise complement 
    Ex: Here's an example that demonstrates the use of all the operators involving bits.
    import java.util.*;
    public class BitwiseDemo {
      public static void main(String[] args) {
        Random rand = new Random(); // generates a random number
        int i = rand.nextInt();
        int j = rand.nextInt();
        pBinInt("-1", -1); // prints out
        pBinInt("+1", +1);
        int maxpos = 2147483647; // maximum integer
        pBinInt("maxpos", maxpos);
        int maxneg = -2147483648; // minimum integer
        pBinInt("maxneg", maxneg);
        pBinInt("i", i);  // prints random integer
        pBinInt("~i", ~i); // and its complement
        pBinInt("-i", -i); // etc...
        pBinInt("j", j);
        pBinInt("i & j", i & j);
        pBinInt("i | j", i | j);
        pBinInt("i ^ j", i ^ j);
        pBinInt("i << 5", i << 5);
        pBinInt("i >> 5", i >> 5);
        pBinInt("(~i) >> 5", (~i) >> 5);
        pBinInt("i >>> 5", i >>> 5);
        pBinInt("(~i) >>> 5", (~i) >>> 5);
        // now for long ints
        long l = rand.nextLong();
        long m = rand.nextLong();
        pBinLong("-1L", -1L);
        pBinLong("+1L", +1L);
        long ll = 9223372036854775807L;
        pBinLong("maxpos", ll);
        long lln = -9223372036854775808L;
        pBinLong("maxneg", lln);
        pBinLong("l", l);
        pBinLong("~l", ~l);
        pBinLong("-l", -l);
        pBinLong("m", m);
        pBinLong("l & m", l & m);
        pBinLong("l | m", l | m);
        pBinLong("l ^ m", l ^ m);
        pBinLong("l << 5", l << 5);
        pBinLong("l >> 5", l >> 5);
        pBinLong("(~l) >> 5", (~l) >> 5);
        pBinLong("l >>> 5", l >>> 5);
        pBinLong("(~l) >>> 5", (~l) >>> 5);
      } // end main
      /*  The two methods at the end, pBinInt( ) and pBinLong( ) take an int or
          a long, respectively, and print it out in binary format along with a
          descriptive string. You can ignore the implementation of these for now.
      */
      // to save typing
      static void pBinInt(String s, int i) {
        System.out.println(
          s + ", int: " + i + ", binary: ");
        System.out.print("   ");  // adds space to a line (no new line)
        for(int j = 31; j >=0; j--) // prints out "i" in binary (32 bytes)
          if(((1 << j) &  i) != 0)
            System.out.print("1");
          else
            System.out.print("0");
        System.out.println(); // go to other line
      } // end pBinInt
      // to save typing
      static void pBinLong(String s, long l) {
        System.out.println(
          s + ", long: " + l + ", binary: ");
        System.out.print("   ");  // adds space to a line (no new line)
        for(int i = 63; i >=0; i--) // prints out "i" in binary (64 bytes)
          if(((1L << i) & l) != 0)
            System.out.print("1");
          else
            System.out.print("0");
        System.out.println(); // go to other line
      } // end pBinLong
    }  // end BitwiseDemo
    In one run of this code I've got
    -1, int: -1, binary:
       11111111111111111111111111111111
    +1, int: 1, binary:
       00000000000000000000000000000001
    maxpos, int: 2147483647, binary:
       01111111111111111111111111111111
    maxneg, int: -2147483648, binary:
       10000000000000000000000000000000
    i, int: 567291140, binary:
       00100001110100000010110100000100
    ~i, int: -567291141, binary:
       11011110001011111101001011111011
    -i, int: -567291140, binary:
       11011110001011111101001011111100
    j, int: 83676736, binary:
       00000100111111001100111001000000
    i & j, int: 13634560, binary:
       00000000110100000000110000000000
    i | j, int: 637333316, binary:
       00100101111111001110111101000100
    i ^ j, int: 623698756, binary:
       00100101001011001110001101000100
    i << 5, int: 973447296, binary:
       00111010000001011010000010000000
    i >> 5, int: 17727848, binary:
       00000001000011101000000101101000
    (~i) >> 5, int: -17727849, binary:
       11111110111100010111111010010111
    i >>> 5, int: 17727848, binary:
       00000001000011101000000101101000
    (~i) >>> 5, int: 116489879, binary:
       00000110111100010111111010010111
    -1L, long: -1, binary:
       1111111111111111111111111111111111111111111111111111111111111111
    +1L, long: 1, binary:
       0000000000000000000000000000000000000000000000000000000000000001
    maxpos, long: 9223372036854775807, binary:
       0111111111111111111111111111111111111111111111111111111111111111
    maxneg, long: -9223372036854775808, binary:
       1000000000000000000000000000000000000000000000000000000000000000
    l, long: -412489331692580499, binary:
       1111101001000110100010110010001101100110110100110101000101101101
    ~l, long: 412489331692580498, binary:
       0000010110111001011101001101110010011001001011001010111010010010
    -l, long: 412489331692580499, binary:
       0000010110111001011101001101110010011001001011001010111010010011
    m, long: -3757276681845227476, binary:
       1100101111011011011110001100001111000111110000011100000000101100
    l & m, long: -3872523919420211156, binary:
       1100101001000010000010000000001101000110110000010100000000101100
    l | m, long: -297242094117596819, binary:
       1111101111011111111110111110001111100111110100111101000101101101
    l ^ m, long: 3575281825302614337, binary:
       0011000110011101111100111110000010100001000100101001000101000001
    l << 5, long: 5247085459546975648, binary:
       0100100011010001011001000110110011011010011010100010110110100000
    l >> 5, long: -12890291615393141, binary:
       1111111111010010001101000101100100011011001101101001101010001011
    (~l) >> 5, long: 12890291615393140, binary:
       0000000000101101110010111010011011100100110010010110010101110100
    l >>> 5, long: 563570460688030347, binary:
       0000011111010010001101000101100100011011001101101001101010001011
    (~l) >>> 5, long: 12890291615393140, binary:
       0000000000101101110010111010011011100100110010010110010101110100

     1.4.4 - Shortcut assignment operators

    Operator Use Equivalent to
    += op1 += op2 op1 = op1 + op2
    -= op1 -= op2 op1 = op1 - op2
    *= op1 *= op2 op1 = op1 * op2
    /= op1 /= op2 op1 = op1 / op2
    %= op1 %= op2 op1 = op1 % op2
    &= op1 &= op2  op1 = op1 & op2
    |= op1 |= op2 op1 = op1 | op2
    ^= op1 ^= op2 op1 = op1 ^ op2
    <<= op1 <<= op2 op1 = op1 << op2
    >>= op1 >>= op2 op1 = op1 >> op2
    >>>= op1 >>>= op2 op1 = op1 >>> op2
    Ex: The next program gives another example of mathematical operators in Java. Just copy, paste to a file named MathOps.java, compile and run it.
    // Demonstrates the mathematical operators.
    import java.util.*;
    public class MathOps {
      // Create a shorthand to save typing:
      static void prt(String s) {
        System.out.println(s);
      }
      // shorthand to print a string and an int:
      static void pInt(String s, int i) {
        prt(s + " = " + i);
      }
      // shorthand to print a string and a float:
      static void pFlt(String s, float f) {
        prt(s + " = " + f);
      }
      public static void main(String[] args) {
        // Create a random number generator,
        // seeds with current time by default:
        Random rand = new Random();
        int i, j, k;
        // '%' limits maximum value to 99:
        j = rand.nextInt() % 100;
        k = rand.nextInt() % 100;
        pInt("j",j);  pInt("k",k);
        i = j + k; pInt("j + k", i);
        i = j - k; pInt("j - k", i);
        i = k / j; pInt("k / j", i);
        i = k * j; pInt("k * j", i);
        i = k % j; pInt("k % j", i);
        j %= k; pInt("j %= k", j);
        // Floating-point number tests:
        float u,v,w;  // applies to doubles, too
        v = rand.nextFloat();
        w = rand.nextFloat();
        pFlt("v", v); pFlt("w", w);
        u = v + w; pFlt("v + w", u);
        u = v - w; pFlt("v - w", u);
        u = v * w; pFlt("v * w", u);
        u = v / w; pFlt("v / w", u);
        // the following also works for
        // char, byte, short, int, long,
        // and double:
        u += v; pFlt("u += v", u);
        u -= v; pFlt("u -= v", u);
        u *= v; pFlt("u *= v", u);
        u /= v; pFlt("u /= v", u);
      }
    }
    In one run of this code I've got
    j = -51
    k = 64
    j + k = 13
    j - k = -115
    k / j = -1
    k * j = -3264
    k % j = 13
    j %= k = -51
    v = 0.92248344
    w = 0.43780828
    v + w = 1.3602917
    v - w = 0.48467517
    v * w = 0.40387088
    v / w = 2.107049
    u += v = 3.0295324
    u -= v = 2.107049
    u *= v = 1.9437178
    u /= v = 2.107049

     1.4.5 - More operators

     
    Operator  Use  Description 
    ?: op1 ? op2 : op3 If op1 is true, returns op2. Otherwise, returns op3
    [] type [] Declares an array of unknown length, which contains type elements.
    [] type[ op2 ] Creates and array with op1 elements. Must be used with the new operator.
    [] op1[ op2 ] Accesses the element at op2 index within the array op1. Indices begin at 0 and extend through the length of the array minus one.
    . op1.op2 Is a reference to the op2 member of op1.
    () op1(params) Declares or calls the method named op1 with the specified parameters. The list of parameters can be an empty list. The list is comma-separated.
    (type) (type) op1 Casts (converts) op1 to type. An exception will be thrown if the type of op1 is incompatible with type.
    new new op1 Creates a new object or array. op1 is either a call to a constructor, or an array specification.
    instanceof op1 instanceof op2 Returns true if op1 is an instance of op2

     1.4.6 - Casting operators

  • The word cast is used in the sense of “casting into a mold.” Java will automatically change one type of data into another when appropriate. For instance, if you assign an integral value to a floating-point variable, the compiler will automatically convert the int to a float. Casting allows you to make this type conversion explicit, or to force it when it wouldn’t normally happen. To perform a cast, put the desired data type (including all modifiers) inside parentheses to the left of any value.

  • Ex:
    void casts() {
      int i = 200;
      long l = (long)i;
      long l2 = (long)200;
    }
    As you can see, it’s possible to perform a cast on a numeric value as well as on a variable. In both casts shown here, however, the cast is superfluous, since the compiler will automatically promote an int value to a long when necessary. However, you are allowed to use superfluous casts in to make a point or to make your code more clear. In other situations, a cast may be essential just to get the code to compile. This happens when you perform a so-called narrowing conversion (that is, when you go from a data type that can hold more information to one that doesn’t hold as much) and you run the risk of losing information. Here the compiler forces you to do a cast, in effect saying “this can be a dangerous thing to do—if you want me to do it anyway you must make the cast explicit.” With a widening conversion an explicit cast is not needed because the new type will more than hold the information from the old type so that no information is ever lost.
  • Java allows you to cast any primitive type to any other primitive type, except for boolean, which doesn’t allow any casting at all. Class types do not allow casting. To convert one to the other there must be special methods. Ex:

  • // What happens when you cast a float
    // or double to an integral value?
    public class CastingNumbers {
      public static void main(String[] args) {
        double
          above = 0.7,
          below = 0.4;
        System.out.println("above: " + above);
        System.out.println("below: " + below);
        System.out.println(
          "(int)above: " + (int)above);
        System.out.println(
          "(int)below: " + (int)below);
        System.out.println(
          "(char)('a' + above): " +
          (char)('a' + above));
        System.out.println(
          "(char)('a' + below): " +
          (char)('a' + below));
      }
    }
    The output is:
    above: 0.7
    below: 0.4
    (int)above: 0
    (int)below: 0
    (char)('a' + above): a
    (char)('a' + below): a
    So, we see that casting from a float or double to an integral value always truncates.

     1.4.7 - Literals

  • Hexadecimal (base 16), which works with all the integral data types, is denoted by a leading 0x or 0X followed by 0—9 and a—f either in upper or lowercase. If you try to initialize a variable with a value bigger than it can hold (regardless of the numerical form of the value), the compiler will give you an error message. Notice in the  code below the maximum possible hexadecimal values for char, byte, and short. If you exceed these, the compiler will automatically make the value an int and tell you that you need a narrowing cast for the assignment.
  • Octal (base 8) is denoted by a leading zero in the number and digits from 0-7. There is no literal representation for binary numbers in C, C++ or Java.
  • A trailing character after a literal value establishes its type. Upper or lowercase L means long, upper or lowercase F means float and upper or lowercase D means double.
  • Exponents use a notation: 1.39 e-47f.  It means 1.39 x 10-47.

  • Ex:
      char c = 0xffff; // max char hex value
      byte b = 0x7f; // max byte hex value
      short s = 0x7fff; // max short hex value
      int i1 = 0x2f; // Hexadecimal (lowercase)
      int i2 = 0X2F; // Hexadecimal (uppercase)
      int i3 = 0177; // Octal (leading zero)
      // Hex and Oct also work with long.
      long n1 = 200L; // long suffix
      long n2 = 200l; // long suffix
      long n3 = 200;
      //! long l6(200); // not allowed
      float f1 = 1;
      float f2 = 1F; // float suffix
      float f3 = 1f; // float suffix
      float f4 = 1e-45f; // 10 to the power
      float f5 = 1e+9f; // float suffix
      double d1 = 1d; // double suffix
      double d2 = 1D; // double suffix
      double d3 = 47e47d; // 10 to the power
    Note that you don't need to use the trailing character when the compiler can figure out the appropriate type. With
    long n3 = 200;
    there's no ambiguity, so an L after the 200 would be superfluous. However, with
    float f4 = 1e-47f; // 10 to the power
    the compiler normally takes exponential numbers as doubles, so without the trailing  f it will give you an error telling you that you must use a cast to convert double to float.

    1.5 - Loops

  • Use the while statement to loop over a block of statements while a boolean expression remains true.

  • while (boolean expression) {
        statements
    }
    Here's a simple example that generates random numbers until a particular condition is met. Try it yourself.
    // Demonstrates the while loop.
    public class WhileTest {
      public static void main(String[] args) {
        double r = 0;
        while(r < 0.99d) {
          r = Math.random();
          System.out.println(r);
        }
      }
    }
  • Use the do-while statement to loop over a block of statements while a boolean expression remains true.

  • do {
        statement
    } while (expression);
    Ex: Add all integers from 0 through 9
    int i = 0;
    int j = 0;
    do {
        j += i;
    } while (i <= 9);
  • The for statement loops over a block of statements and includes an initialization statement, a termination condition statement, and an increment statement.

  • for (initialization ; termination ; increment) {
        statements
    }
    for loops are usually used for “counting” tasks:
    // Demonstrates "for" loop by listing
    // all the ASCII characters.
    public class ListCharacters {
      public static void main(String[] args) {
      for( char c = 0; c < 128; c++)
        if (c != 26 )  // ANSI Clear screen
          System.out.println(
            "value: " + (int)c +
            " character: " + c);
      }
    }
  • Note that the variable c is defined at the point where it is used, inside the control expression of the for loop, rather than at the beginning of the block denoted by the open curly brace. The scope of c is the expression controlled by the for.
  • Traditional procedural languages like C require that all variables be defined at the beginning of a block so when the compiler creates a block it can allocate space for those variables. In Java and C++ you can spread your variable declarations throughout the block, defining them at the point that you need them. This allows a more natural coding style and makes code easier to understand.

  • You can define multiple variables within a for statement, but they must be of the same type:
    for(int i = 0, j = 1;
        i < 10 && j != 11;
        i++, j++)
      /* body of for loop */;
    The int definition in the for statement covers both i and j. The ability to define variables in the control expression is limited to the for loop. You cannot use this approach with any of the other selection or iteration statements.

    1.6 - Decision-Making

  • A basic if statement whose single statement block is executed if the boolean expression is true.

  • if (boolean expression) {
        statements
    }
    Ex:
    int Ten = 0;
    if (Ten != 10) {
        System.out.println(Ten + "is not Ten");
        Ten += 1;
    }
    System.out.println("All right." + Ten + "is equal to 10.");
  • An if statement with one boolean expression and two statement blocks. The first statement block is executed if the boolean expression is true and the second is executed if the boolean expression is false.

  • if (boolean expression) {
        statements
    } else {
        statements
    }
  • An if statement with multiple expressions and an else statement.

  • if (boolean expression) {
        statements
    } else if (boolean expression) {
        statements
    } else {
        statements
    }
    Ex: A program to assign a grade based on the value of a test score: an A for a score of 90% or above, a B for a score of 80% or above, and so on:
    public class IfElseDemo {
        public static void main(String[] args) {
            int testscore = 76;
            char grade;
            if (testscore >= 90) {
                grade = 'A';
            } else if (testscore >= 80) {
                grade = 'B';
            } else if (testscore >= 70) {
                grade = 'C';
            } else if (testscore >= 60) {
                grade = 'D';
            } else {
                grade = 'F';
            }
            System.out.println("Grade = " + grade);
        }
    }
    This code yields the output
    Grade = C
  • The switch statement evaluates and integer expression and executes the appropriate case statement.

  • switch (integer expression) {
        case integer expression:
           statements
             break;
        ...
        default:
           statements
             break;
    }
    Here's an example that creates letters randomly and determines whether they're vowels or consonants. Try it on your machine.
    // Demonstrates the switch statement.
    public class VowelsAndConsonants {
      public static void main(String[] args) {
        for(int i = 0; i < 100; i++) {
          char c = (char)(Math.random() * 26 + 'a');
          System.out.print(c + ": ");
          switch(c) {
          case 'a':
          case 'e':
          case 'i':
          case 'o':
          case 'u':
                    System.out.println("vowel");
                    break;
          case 'y':
          case 'w':
                    System.out.println(
                      "Sometimes a vowel");
                    break;
          default:
                    System.out.println("consonant");
          }
        }
      }
    }
    Since Math.random( ) generates a value between 0 and 1, you need only multiply it by the upper bound of the range of numbers you want to produce (26 for the letters in the alphabet) and add an offset to establish the lower bound.

    1.7 - Multi-Level Break

  • Java has no goto statement
  • The continue statement and labeled blocks. Ex:

  •   test:  for (int i = fromIndex; i + max1 <= max2; i++) {
               if (charAt(i) == c0) {
                   for (int k = 1; k < max1; k++) {
                       if (charAt(i+k) != str.charAt(k)) {
                           continue test;
                       }
                   }     /*  end of inner for loop  */
               }
           }             /*  end of outer for loop  */
      Here, the continue test statement is inside a for loop nested inside another for loop.   By referencing the label test, the continue statement passes control to the outer for statement.
  • Here is a demonstration of labeled break and continue statements with while loops:

  • // Java's "labeled while" loop.
    public class LabeledWhile {
      public static void main(String[] args) {
        int i = 0;
        outer:
        while(true) {
          prt("Outer while loop");
          while(true) {
            i++;
            prt("i = " + i);
            if(i == 1) {
              prt("continue");
              continue;
            }
            if(i == 3) {
              prt("continue outer");
              continue outer;
            }
            if(i == 5) {
              prt("break");
              break;
            }
            if(i == 7) {
              prt("break outer");
              break outer;
            }
          }
        }
      }
      static void prt(String s) {
        System.out.println(s);
      }
    }
    The output is
    Outer while loop
    i = 1
    continue
    i = 2
    i = 3
    continue outer
    Outer while loop
    i = 4
    i = 5
    break
    Outer while loop
    i = 6
    i = 7
    break outer

    1.8 - Exception-Handling

  • 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.
       throw exception;
    Then, within one or more catch blocks, you write the program logic that deals with all the special cases.
    try {
        statements
    } catch (exceptiontype name) {
        statements
    } catch (exceptiontype name) {
        statements
    } finally {
        statements
    }
  • 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.
  •  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.

    1.9 - Command Line Arguments

  • The HelloWorld class contains one method, the main method. As in C the main method is where an application begins executing. The method is declared public meaning that the method can be called from anywhere. It is declared static meaning that all instances of this class share this one method. (Don't worry. We'll come back to these definitions  later.) It is declared void which means, as in C, that this method does not return a value. Finally we pass any command line arguments to the method in an array of Strings called args. In this simple program there aren't any command line arguments though.
  • Finally when the main method is called it does exactly one thing: print "Hello World" to the standard output, generally a terminal monitor or console window of some sort. This is accomplished by the System.out.println method. To be more precise this is accomplished by calling the println() method of the static out field belonging to the System class; but for now we'll just treat this as one method.
  • Here we show a simple example of use of command line arguments. args is a special array that holds the command line arguments. args[0] holds the first command line argument. args[1] holds the second command line argument, args[2] holds the third command line argument and so on.  Ex:

  •   public class Echo {
          public static void main (String[] args) {
              for (int i = 0; i < args.length; i++)
                  System.out.println(args[i]);
          }
      }
      If we enter
         java Echo Drink Hot Java
    at runtime (Runtime is when we type java File. Compile time is when we type javac File.java), we get:
       Drink
       Hot
      Java

    1.10 - Arrays

  • Arrays is a collection of objects with a given data type, primitive, or not. One declares an array by using the [] operator. To allocate memory one uses the new operator and enters the number of objects inside [].   Ex: An array of Point objects.

  •     Point  myPoints[];   // declaration
        myPoints = new Point[10]; // allocation of memory
        for (int i = 0;  i < 10;  i++) {  // real allocation of objects
            myPoints[i] = new Point();
        }
  • The length()accessor method: One way to avoid errors in accessing the number of elements of an array is to use the array's built-in length member. Ex:

  •    howMany = myPoints.length(); // assign the value 10 to the howMany variable
      Ex: This example shows a safe way of using the maximum index of an array.
    float[] squares = new float[101];
    for (int i=0, i < squares.length; i++) {
      squares[i] = i*i;
    }
  • To initialize arrays one may use some shortcuts. Ex:

  •  int[] k = new int[3];
      float[] yt = new float[7];
      int[] k = {1, 2, 3};
      float[] yt = {0.0f, 1.2f, 3.4f, -9.87f, 65.4f, 0.0f, 567.9f};
  • A practical example: let's consider a class that counts the occurrences of the digits 0-9 in decimal expansion of the number pi, 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]);
        }
      }
    }
    In a run in my machine, I've got
    0: 9872
    1: 9850
    2: 10205
    3: 9995
    4: 10081
    5: 9977
    6: 9919
    7: 10088
    8: 10034
    9: 9979
  • Two dimensional arrays: Consider the array below.
  •  
    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]
    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;
          }
        }
      }
    }
  • Similar construction can be used for arrays of larger dimensions.
  • 1.11 - Strings and I/O streams

  • Ex: We declare and initialize a string like this:

  •   String hello = "Hello world!";  // instantiates an object of the String class and
                                      // initializes it with a character string containing
                                      // the character representation of "Hello world!".
  • The method System.out.println allows to perform String concatenation: Ex: To print There are 23 characters in the file, we use

  •  int num = 23;
      System.out.println("There are " + num + " characters in the file.");
  • The length()accessor method yields the number of characters in the string.
  • 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. Ex:
    import java.io.*;
    class PersonalHello {
      public static void main (String args[])
        {
          byte name[] = new byte[100];   // array of bytes that will hold the user's name.
          int nr_read = 0;
          System.out.println("What is your name?"); // query requesting the user's name.
          try {
            // read the user's name. This method takes a byte array as an
            // argument, and places whatever the user types in that byte array.
            nr_read = System.in.read(name);
            System.out.print("Hello ");
            System.out.write(name,0,nr_read);  // print the user's name.
          }
          catch (IOException e) {
            System.out.print("I'm Sorry.  I didn't catch your name.");
          }
        }
    }
    In this code 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. It internally checks the length of the array it's been passed using the name.length property. Test this program yourself.
  • Reading numbers: a lot of times you'll want to ask the user for a number as input. All user input comes in as strings so we need to convert the string into a

  • number. Ex: We write a getNextInteger() method that will accept an integer from the user:
      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
  • Sometimes you may need to read text and numbers on the same line. For this purpose Java provides the StreamTokenizer class.

  • 1 - Open a FileOutputStream using a line like
         FileOutputStream fout =  new FileOutputStream("test.out");
         This line initializes the FileOutputStream with the name of the file you want to write into.
       2 - Convert the FileOutputStream into a PrintStream using a statement like
         PrintStream myOutput = new PrintStream(fout);
         The PrintStream is passed the FileOutputStream from step 1.
       3 - Instead of using 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.
  • Ex: The program below makes the Fahrenheit to Celsius conversion and writes the output to a file:

  • // 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
    }
    The test.out file will contain the output
    0.0 -17.77777777777778
    20.0 -6.666666666666667
    40.0 4.444444444444445
    60.0 15.555555555555555
    80.0 26.666666666666668
    100.0 37.77777777777778
    120.0 48.888888888888886
    140.0 60.0
    160.0 71.11111111111111
    180.0 82.22222222222223
    200.0 93.33333333333333
    220.0 104.44444444444444
    240.0 115.55555555555556
    260.0 126.66666666666667
    280.0 137.77777777777777
    300.0 148.88888888888889
  • Ex: Reading a text file. He is a code which imitates the Unix cat utility that reads and prints out  text files.

  • // 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
    }


    1.12 - Memory Management and Garbage Collection

  • Java has no pointers. Also, the Java run-time system checks all array indexing to ensure indices are within the bounds of the array.
  • The sole reason for the existence of the garbage collector is to recover memory that your program is no longer using.
  • In C++ if an object is created as a local (i.e., on the stack—not possible in Java), then the destruction happens at the closing curly brace of the scope in which the object was created. If the object was created using new (like in Java) the destructor is called when the programmer calls the C++ operator delete (which doesn’t exist in Java). If the C++ programmer forgets to call delete, the destructor is never called and you have a memory leak, plus the other parts of the object never get cleaned up. This kind of bug can be very difficult to track down.
  • In contrast, Java doesn’t allow you to create local objects—you must always use new.  But in Java, there’s no “delete” to call to release the object since the garbage collector releases the storage for you. The Java memory manager keeps track of references to objects. When an object has no more references, the object is a candidate for garbage collection.  Ex:

  •     class ReverseString {
          public static String reverseIt(String source) {
              int i, len = source.length();
              StringBuffer dest = new StringBuffer(len);
              for (i = (len - 1); i >= 0; i--) {
                  dest.appendChar(source.charAt(i));
              }
              return dest.toString();
          }
        }
        The variable dest is used as a temporary object reference during the execution of the reverseIt method. When dest goes out of scope (the reverseIt method returns),  the reference to that object has gone away and it's then a candidate for garbage collection.

    1.13 - Features removed from C and C++

  • Java has no preprocessor, no #define and related capabilities, no typedef, and absent  those features, no longer any need for header files. Instead of header files, Java language source files provide the definitions of other classes and their methods. 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.
  • Java has no structures or unions as complex data types, only classes.

  •   Ex: (1) A class called Point:
      class Point extends Object {
            double  x;
            double  y;
            . . .      //  methods to access the instance variables
        }
        (2) A class called Rectangle, that uses objects of the Point class as instance variables.
        class Rectangle extends Object {
            Point  lowerLeft;
            Point  upperRight;
            . . .      //  methods to access the instance variables
        }
  • No more Functions: anything you can do with a function you can do just as well by defining a class and creating methods for that class.

  •   Ex: (1) Consider the Point class from above. We've added public methods to set and access the instance variables:
      class Point extends Object {
            double  x;
            double  y;

            public void setX(double x) {
                this.x = x;
            }
            public void setY(double y) {
                this.y = y;
            }
            public double x() {
                return x;
            }
            public double y() {
                return y;
            }
        }
        (2) Here's how you'd use objects of the Point class from within, say, an object of the Rectangle class:
        class Rectangle extends Object {
            Point  lowerLeft;
            Point  upperRight;
            public void setEmptyRect() {
                lowerLeft.setX(0.0);
                lowerLeft.setY(0.0);
                upperRight.setX(0.0);
                upperRight.setY(0.0);
            }
        }

  • Java has  no Multiple Inheritance. The desirable features of multiple inheritance are provided by interfaces. An interface is not a definition of an object. Rather, it's a definition of a set of methods that one or more objects will implement. An important issue of interfaces is that they declare only methods and constants. No variables may be defined in interfaces.
  • Java has no goto statement.
  • Java has no  Operator Overloading. The effects of operator overloading can be just as easily achieved by declaring a class, appropriate instance variables, and appropriate methods to manipulate those variables.
  • Java has no Automatic Coercions.  Ex:

  •   int  myInt;
      double  myFloat = 3.14159;
      myInt = myFloat;    // Java compiler error! Lost of precision.
      myInt = (int)myFloat;   // OK. Cast into Integer.
  • The single biggest difference between Java and C or C++ is that Java's memory model  eliminates the possibility of overwriting memory and corrupting data. Instead of pointer arithmetic, Java has true arrays and strings, which means that the interpreter can check array and string indexes. In addition, a programmer can't write code that turns an arbitrary integer into an object reference by casting.
  • The Java compiler is written in Java. The Java run-time system is written in ANSI C.

  •  

    2 - Objects

    2.1 - Basics

  • In the programming implementation of an object, its state is defined by its instance variables.
  • An object's behavior is defined by its methods. Each calculation part of a program is called a method. Methods are logically the same as C's functions, Pascal's procedures and functions, and Fortran's functions and subroutines.
  • The return statement sends a value back to the calling method. The type of this value must match the declared type of the method as in C.
  • In general any line that looks like Text(arg1, arg2) or text(arg1) or text() is a method call.
  • Scope: A variable's scope is the block of code within which the variable is accessible and determines when the variable is created and destroyed.
  • 2.2 - Classes

  • A class is a software construct that defines the instance variables and methods of an object. A class in and of itself is not an object. A class is a template that defines how an object will look and behave when the object is created or instantiated from the specification declared by the class.

  •   Ex:
      class Point extends Object {
          public double  x;    /*  instance variable  */
          public double  y;    /*  instance variable  */
        }
  • Both syntactically and logically everything in Java happens inside a class.  Methods are defined inside the classes they belong to. This may be a little confusing to C++ programmers who are used to defining all but the simplest methods outside the class block. Even basic data primitives like integers often need to be incorporated into classes before you can do many useful things with them. The class is the fundamental unit of Java programs, not source code files like in C.
  • Ex: Consider the Java program:

  • class HelloWorld {
      public static void main (String args[]) {
        System.out.println("Hello World");
      }
    }
    class GoodbyeWorld {
      public static void main (String args[]) {
        System.out.println("Goodbye Cruel World!");
      }
    }
    Save this code in a single file called hellogoodbye.java in your directory, and compile it with the command javac hellogoodbye.java. Then list the contents of the directory. You will see that the compiler has produced two separate class files, HelloWorld.class and GoodbyeWorld.class.
    The second class is a completely independent program. Type java GoodbyeWorld and then type java HelloWorld. These programs run and execute
    independently of each other although they exist in the same source code file.
  • The primary distinguishing feature of OOP (Object Oriented Programming) languages is the class. A class is a data structure that can associate the methods which act on an object with the object itself. In pre-OOP languages methods and data were separate. In OOP languages they are all part of classes. 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.
  • 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.
  • 2.3 - Instantiating an Object from its 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);
  • 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.
  • For the class Point defined above we would use

  •    Point  myPoint;          //  declares a variable to refer to a Point object
      myPoint = new Point();   //  allocates an instance of  a Point object
    and access the variables of this Point object:
          myPoint.x = 10.0;
       myPoint.y = 25.7;
      This works because the instance variables of Point were declared public in the class declaration, otherwise the Point class declaration would need to provide accessor  methods to set and get its variables.

    2.4 - Constructors

  • Methods with the same name as the class as in the code fragment are called constructors. The first method most classes need is a constructor. A constructor creates a new instance of the class. It initializes all the variables and does any work necessary to prepare the class to be used.

  •   Ex:
        class Point extends Object {
            public double  x;    /*  instance variable  */
            public double  y;    /*  instance variable  */

            Point() {        /*  constructor to initialize to default zero value  */
                x = 0.0;
                y = 0.0;
            }
                            /*  constructor to initialize to specific value  */
            Point(double x, double y) {
                this.x = x;    /*  set instance variables to passed parameters  */
                this.y = y;
            }
        }

  • Use of Point:

  •         Point  lowerLeft;
        Point  upperRight;
        lowerLeft = new Point();        /*  initialize to default zero value  */
        upperRight = new Point(100.0, 200.0);  /*  initialize to non- zero   */
  • Often, constructors are necessary.

  •   Ex:
        class Rectangle extends Object {
           private Point lowerLeft;
           private Point upperRight;

            Rectangle() {
                lowerLeft = new Point();
                upperRight = new Point();
            }
               . . .
             instance methods appear in here
                . . .
        }
      In this example, the Rectangle() constructor is vitally necessary to ensure that the two Point objects are instantiated at the time a Rectangle object is instantiated, otherwise, the Rectangle object would subsequently try to reference points that have  not yet been allocated, and would fail.

  •  To learn more from examples, lets turn to our website class. In the line 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.

    2.5 - Static and Final Variables

  • The static keyword.  When you say something is static, it means that data or method is not tied to any particular object instance of that class. So even if you’ve never created an object of that class you can call a static method or access a piece of static data. With ordinary, non-static data and methods you must create an object and use that object to access the data or method, since non-static data and methods must know the particular object they are working with. Of course, since static methods don’t need any objects to be created before they are used, they cannot directly access non-static members or methods by simply calling those other members without referring to a named object (since non-static members and methods must be tied to a particular object).

  • Ex:
    class StaticTest {
        static int i = 47;
    }
    Now even if you make two StaticTest objects, there will still be only one piece of storage for StaticTest.i. Both objects will share the same i. Consider:
    StaticTest st1 = new StaticTest();
    StaticTest st2 = new StaticTest();
    At this point, both st1.i and st2.i have the same value of 47 since they refer to the same piece of memory.
  • There are two ways to refer to a static variable. As indicated above, you can name it via an object, by saying, for example, st2.i. You can also refer to it directly through its class name, something you cannot do with a non-static member. (This is the preferred way to refer to a static variable since it emphasizes that variable’s static nature.)

  • StaticTest.i++;
    The ++ operator increments the variable. At this point, both st1.i and st2.i will have the value 48.
  • Similar logic applies to static methods. You can refer to a static method either through an object as you can with any method, or with the special additional syntax ClassName.method( ). Ex:

  • class StaticFun {
      static void incr() { StaticTest.i++; }
    }
    You can see that the StaticFun method incr( ) increments the static data i. You can call incr( ) in the typical way, through an object:
    StaticFun sf = new StaticFun();
    sf.incr();
    Or, because incr( ) is a static method, you can call it directly through its class:
    StaticFun.incr();
  • While static, when applied to a data member, definitely changes the way the data is created (one for each class vs. the non-static one for each object), when applied to a method it’s not so dramatic. An important use of static for methods is to allow you to call that method without creating an object. This is essential, as we will see, in defining the main( ) method that is the entry point for running an application.
  • The final keyword. You can declare a variable in any scope to be final. The value of a final variable cannot change after it has been initialized. Ex:

  • final int aFinalVar = 0;
    A final variable is like a constant (const) variable in C.
  • A field that is both static and final has only one piece of storage that cannot be changed.
  •  With a primitive data type, final makes the value a constant, but with an object reference, final makes the reference a constant. Once the reference is initialized to an object, it can never be changed to point to another object. However, the object itself can be modified; Java does not provide a way to make any arbitrary object a constant. (You can, however, write your class so that objects have the effect of being constant.) This restriction includes arrays, which are also objects.

  • Ex:
    //:  FinalData.java
    // The effect of final on fields.
    class Value {
      int i = 1;
    }
    public class FinalData {
      // Can be compile-time constants
      final int i1 = 9;
      static final int VAL_TWO = 99;
      // Typical public constant:
      public static final int VAL_THREE = 39;
      // Cannot be compile-time constants:
      final int i4 = (int)(Math.random()*20);
      static final int i5 = (int)(Math.random()*20);
      Value v1 = new Value();
      final Value v2 = new Value();
      static final Value v3 = new Value();
      // Arrays:
      final int[] a = { 1, 2, 3, 4, 5, 6 };
      public void print(String id) {
        System.out.println(
          id + ": " + "i4 = " + i4 +
          ", i5 = " + i5);
      }
      public static void main(String[] args) {
        FinalData fd1 = new FinalData();
        //! fd1.i1++; // Error: can't change value
        fd1.v2.i++; // Object isn't constant!
        fd1.v1 = new Value(); // OK -- not final
        for(int i = 0; i < fd1.a.length; i++)
          fd1.a[i]++; // Object isn't constant!
        //! fd1.v2 = new Value(); // Error: Can't
        //! fd1.v3 = new Value(); // change reference
        //! fd1.a = new int[3];
        fd1.print("fd1");
        System.out.println("Creating new FinalData");
        FinalData fd2 = new FinalData();
        fd1.print("fd1");
        fd2.print("fd2");
      }
    }
  • Since i1 and VAL_TWO are final primitives with compile-time values, they can both be used as compile-time constants and are not different in any important way. VAL_THREE is the more typical way you’ll see such constants defined: public so they’re usable outside the package, static to emphasize that there’s only one, and final to say that it’s a constant. Note that final static primitives with constant initial values (that is, compile-time constants) are named with all capitals by convention, with words separated by underscores (This is just like C constants, which is where the convention originated.) Also note that i5 cannot be known at compile-time, so it is not capitalized.
  • Just because something is final doesn’t mean that its value is known at compile-time. This is demonstrated by initializing i4 and i5 at run-time using randomly generated numbers. This portion of the example also shows the difference between making a final value static or non-static. This difference shows up only when the values are initialized at run-time, since the compile-time values are treated the same by the compiler. (And presumably optimized out of existence.) The difference is shown in the output from one run:

  • fd1: i4 = 15, i5 = 9
    Creating new FinalData
    fd1: i4 = 15, i5 = 9
    fd2: i4 = 10, i5 = 9
    Note that the values of i4 for fd1 and fd2 are unique, but the value for i5 is not changed by creating the second FinalData object. That’s because it’s static and is initialized once upon loading and not each time a new object is created.

    2.6 - The this variable

  • this refers to the object you're "in" right now. In the two-parameter Point method, this.x means the x instance variable of this object, rather than the x parameter to the Point method.
  • If you have two objects of the same type called a and b, you might wonder how it is that you can call a method f( ) for both those objects:

  • class Banana { void f(int i) { /* ... */ } }
    Banana a = new Banana(), b = new Banana();
    a.f(1);
    b.f(2);
    If there's only one method called f( ), how can that method know whether it's being called for the object a or b?
    To allow you to write the code in a convenient object-oriented syntax in which you “send a message to an object,” the compiler does some undercover work for you. There's a secret first argument passed to the method f( ), and that argument is the reference to the object that's being manipulated. So the two method calls above become something like:
    Banana.f(a,1);
    Banana.f(b,2);
    This is internal and you can't write these expressions and get the compiler to accept them, but it gives you an idea of what's happening.
    Suppose you're inside a method and you'd like to get the reference to the current object. Since that reference is passed secretly by the compiler, there's no identifier for it. However, for this purpose there's a keyword: this. The this keyword—which can be used only inside a method—produces the reference to the object the method has been called for. You can treat this reference just like any other object reference. Keep in mind that if you're calling a method of your class from within another method of your class, you don't need to use this; you simply call the method. The current this reference is automatically used for the other method. Thus you can say:
    class Apricot {
      void pick() { /* ... */ }
      void pit() { pick(); /* ... */ }
    }
    Inside pit( ), you could say this.pick( ) but there's no need to. The compiler does it for you automatically. The this keyword is used only for those special cases in which you need to explicitly use the reference to the current object. For example, it's often used in return statements when you want to return the reference to the current object:
    // Leaf.java
    // Simple use of the "this" keyword
    public class Leaf {
      int i = 0;
      Leaf increment() {
        i++;
        return this;
      }
      void print() {
        System.out.println("i = " + i);
      }
      public static void main(String[] args) {
        Leaf x = new Leaf();
        x.increment().increment().increment().print();
      }
    }
    Because increment( ) returns the reference to the current object via the this keyword, multiple operations can easily be performed on the same object.

    2.7 - Methods and Messaging and Access control

     2.7.1 - Public, Private and Protected

  • public methods and instance variables are available to any other class anywhere.
  • protected means that instance variables and methods so designated are accessible only to subclasses of that class, and nowhere else.
  • private methods and instance variables are accessible only from within the class in which they're declared--they're not available even to their subclasses.

  •   Ex:
      class Point extends Object {
            private double  x;    /*  instance variable  */
            private double  y;    /*  instance variable  */

            Point() {    /*  constructor to initialize to zero  */
                x = 0.0;
                y = 0.0;
            }
                               /*  constructor to initialize to specific value  */
            Point(double x, double y) {
                this.x = x;
                this.y = y;
            }
            public void setX(double x) {    /*  accessor method  */
                this.x = x;
            }
            public void setY(double y) {    /*  accessor method  */
                this.y = y;
            }
            public double getX() {    /*  accessor method  */
                return x;
            }
            public double getY() {    /*  accessor method  */
                return y;
            }
        }
        Point  myPoint;          //  declares a variable to refer to a Point object
        myPoint = new Point();   //  allocates an instance of  a Point object
        myPoint.setX(10.0);      //  sets the x variable via the accessor method
        myPoint.setY(25.7);

  • By making instance variables public, you are exposing some of the details of the implementation of the class, thereby providing higher efficiency and conciseness of expression at the possible expense of hindering future maintenance efforts. By hiding details of the internal implementation of a class, you have the potential to change the implementation of the class in the future without breaking any code that uses that class.
  •  2.7.2 - toString Methods

  • Print methods are common in some languages but most Java programs operate differently. You can use 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);
      }
    }

    2.8 - Finalizers

  • Optional. Illustration of a finalize method in a class.

  •   /**
          * Close the stream when garbage is collected.
          */
        protected void finalize() {
            try {
                file.close();
            } catch (Exception e) {
            }
        }
      In this case, the 'finalize' method merely closes an I/O file stream that was used by  the object, to ensure that the file descriptor for the stream is closed.

    2.9 - Subclassing

  • Mechanism by which new and enhanced objects can be defined in terms of existing objects.

  •   Ex: a zebra is a horse with stripes. Thus, in Java,
      class Zebra extends Horse {
         . . .   Your new instance variables and new methods go here
        }
      Zebra is said to be a derived class -- it's derived from Horse, which is called a base class.
  • Another example:

  •       class Point extends Object {
            protected double  x;    /*  instance variable  */
            protected double  y;    /*  instance variable  */

            Point() {    /*  constructor to initialize to zero  */
                x = 0.0;
                y = 0.0;
            }
        }
        class ThreePoint extends Point {
            protected double z;    /*  the z coordinate of the point  */

            ThreePoint() {      /*  default constructor  */
                x = 0.0;        /*  initialize the coordinates  */
                y = 0.0;
                z = 0.0;
            }
            ThreePoint(double x, double y, double z) {/* specific constructor */
                this.x = x;        /*  initialize the coordinates  */
                this.y = y;
                this.z = z;
            }
        }
      The x and y instance variables are inherited from the original Point class, so there's  no need to declare them in ThreePoint. However, notice we had to make Point's instance variables protected instead of private as in the previous examples. Had we left Point's  instance variables private, even its subclasses would be unable to access them, and the compilation would fail.

    2.10 - Class Variables and Class Methods

  • Normally, variables you declare in a class definition are instance variables -- there is one of those variables in every separate object that's created (instantiated) from the class. A class variable, on the other hand, is local to the class itself -- there's only a  single copy of the variable and it's shared by every object you instantiate from the class.
  • Final variables are variables to be kept constant. Ex:

  •   final int aFinalVar = 0;
      final int blankfinal;
      . . .
      blankfinal = 0;
  • To declare class variables and class methods, you declare them as static. Ex:

  •       class Rectangle extends Object {
            static  final int version = 2;
            static  final int revision = 0;
        }
  •   Every instance of Rectangle that you create from this class will share these same variables. Notice they're also defined as final because you want them to be constants. Static methods have only one instance per class rather than one instance per object. All objects of the same class share a single copy of a static method. By default methods are not static.
  • Recursive Methods. Ex: Calculating the factorial of a number

  •   public static long factorial (int n) {
        if (n == 0) {
          return 1;
        }
        else {
          return n*factorial(n-1);
        }
      }
  • With the this keyword in mind, you can more fully understand what it means to make a method static. It means that there is no this for that particular method. You cannot call non-static methods from inside static methods (although the reverse is possible), and you can call a static method for the class itself, without any object. In fact, that's primarily what a static method is for. It is as if you're creating the equivalent of a global function (from C). Except global functions are not permitted in Java, and putting the static method inside a class allows it access to other static methods and to static fields.
  • 2.11 - Abstract Methods

  • An abstract superclass is a class in which you define methods that aren't actually implemented by that class--they only provide place-holders such that subsequent subclasses must override those methods and supply their actual implementation.

  •   Ex:
      abstract class Graphic extends Object {
        protected Point lowerLeft;            //  lower left of bounding box
        protected Point upperRight;           //  upper right of bounding box
                         . . .
                    more instance variables
                         . . .
        public void setPosition(Point ll, Point ur) {
            lowerLeft = ll;
            upperRight = ur;
        }
        abstract void drawMyself();   //  abstract method
        }
      }
  • You can't instantiate the Graphical class, because it's declared abstract. You can only instantiate a subclass of it.
  • Example of use for a Rectangle Object:

  •   abstract class Rectangle extends Graphical {
        void drawMySelf() {         //  really does the drawing
            moveTo(lowerLeft.x, lowerLeft.y);
            lineTo(upperRight.x, lowerLeft.y);
            lineTo(upperRight.x, upperRight.y)
            lineTo(lowerLeft.x, upperRight.y);
                         . . .
                      and so on and so on
                         . . .
        }
      }

    2.12 - A worked example: Complex Numbers

  • Unfortunately no popular computer language other than Fortran provides them as a built-in data type. It was recently added to the C++ as a standard library. Let's see how we might implement them in Java.

  • Since they 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(u, v);
      }
      // 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 x and y 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");
      }
    }
  • Here we agree on one thing. In C++ one can perform operator overloading and get the complex objects manipulated with the usual operators (+, -, /, *, =, etc.).
  • Our printing in the last program was quite stilted because we needed to break a complex number into its real and imaginary parts, print them, and then put it all back together again. Wouldn't it be nice if we could just write:

  •     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.

    2.13 - Polymorphism

  • So far our methods just do arithmetic on two complex numbers. It's not uncommon to want to multiply a complex number by a real number. To add this capability to our class we'll add the following method:

  •   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.

    2.14 - Scope: Calling the Complex Class From External Classes

  • Until now we've stored almost every program in a single file. This becomes unwieldy as programs grow large. It becomes impossible to manage when more than one person is working on a program. It also loses out on one of the key benefits of OOP, code reusability. As long as all the code for a program is stored in one file, you can't reuse code except by cut and paste, just like in a non-object oriented language.
  • 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.
    1. No file should contain more than one public class. This means that our Hello World, Goodbye World example is no longer valid because each of the classes was public.
    2. All files should have the same name as their single public class followed by the extension ".java".
    3. Source code files should be stored in the same directory as their compiled .class file. This is so the Java compiler can find the appropriate definitions and interfaces for a class when the class is referred to in a different file.
    4. Source code and .class files should be in a directory that's part of the $CLASSPATH environment variable.
    You can split the example of the previous sections into two separate files, each of which contains one class. Begin by creating a file that contains the Complex class. Save this file as Complex.java. 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.
     

    3 - Multithreading in Java

    3.1 - Basics

  • Threads are an essential keystone of Java. The Java library provides a Thread class that supports a rich collection of methods to start a thread, run a thread, stop a thread, and check on a thread's status. Ex:

  •    public class SimpleThread extends Thread {
        public SimpleThread(String str) {
        // Calls a superclass constructor. Used only to set the Thread's name.
            super(str);
        }
        public void run() {   // Run method. The heart of any Thread.
            for (int i = 0; i < 10; i++) {
       // Get thread's name
                System.out.println(i + " " + getName());
                try {
        // Sleeps for a random interval of up to 1 second.
                    sleep((long)(Math.random() * 1000));
                } catch (InterruptedException e) {}
            }
            System.out.println("DONE! " + getName());
        }
     }
      public class TwoThreadsTest {
          public static void main (String[] args) {
              new SimpleThread("Jamaica").start();
              new SimpleThread("Fiji").start();
          }
      }

    3.2 - Thread Synchronization

  • Methods within a class that are declared synchronized do not run concurrently. Ex:

  •   public synchronized void stop() {
        if (kicker != null) {
            kicker.stop();
            kicker = null;
        }
      }
      private synchronized void startSort() {
        if (kicker = = null || !kicker.isAlive()) {
            kicker = new Thread(this);
            kicker.start();
        }
      }
      The stop and startSort methods are declared to be synchronized -- they can't run concurrently, enabling them to maintain consistent state in the shared kicker variable. When a synchronized method is entered, it acquires a monitor on the current object. The monitor precludes any other synchronized methods in that object from running. When a synchronized method returns by any means, its monitor is released. Other synchronized methods within the same object are now free to run.
  • If you wish your objects to be thread-safe, any methods that may change the values of instance variables should be declared synchronized.
  • 4 - Applets

    4.1 - Basics

  • Ex:

  • import java.applet.Applet;    // import packages from basic release.
    import java.awt.Graphics;    //  awt stands for "advanced window toolkit" or "applet window toolkit".
    public class HelloWorldApplet extends Applet { // extends  indicates that this class is a subclass of the Applet class
      public void paint(Graphics g) {
        g.drawString("Hello world!", 50, 25);
      }
    }
    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.
    You will need an HTML code (ex: HelloWorldApplet.HTML) with a single line, like that:
    <APPLET codebase="classes" code="HelloWorldApplet.class" width=200 height=200 ></APPLET>
    Now you can see the applet either using a web browser and opening the HTML file, ot by typing appletviewer HelloWorldApplet.HTML in a DOS or UNIX window.   The height and width are in pixels.
  • In the code one sees that 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.
  • 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.
  • 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.
  • The CODE parameter tells the browser where to look for the compiled .class file.

  • 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 you can also specify an HSPACE and a VSPACE in pixels to set the amount of blank space between an applet and the surrounding text.

    4.2 - Passing Parameters to Applets

  • The area between the opening and closing APPLET tag is also used to pass parameters to applets. This is done through the use of the PARAM HTML tag and the getParameter method of the java.applet.Applet class. Ex:

  • 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="Hello World!"></APPLET>
    </BODY>
    </HTML>
    This allows you to change the output of the applet without changing or recompiling the code. 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  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.
  • 4.3 - Events and Applets

  • Whenever an event occurs, the applet below responds by printing the name of the event at the command line.

  • 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");
       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. Double-click 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.
  •  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 import java.awt.*. The compiler is smart enough to only link in those that it actually uses.
  • 4.4 - Making a List

  • It is extremely bad form to use System.out.println() in an applet. For better layout one can 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;
      }
    }

    4.5 - The Events

  • List of methods:

  • - init
    The init() method is called when your applet begins executing. Generally you use this method to set up any data structures or perform any tasks you need to get ready to run the applet.
    public void init() {
        System.out.println("init event");
     }
    - paint
    We've already seen the 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
    A 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.  Once the user returns to the page the start() method is called.
     public void stop() {
        theList.addItem("stop event");
      }
    - start
    The start() method is called when a user brings their attention back to an applet, for instance after maximizing a window or returning to the applet's page. It is called after the init() method. Initialization code that needs to be performed every time an applet is restarted should be put here.
     public void start() {
        theList.addItem("start event");
      }
    - destroy
    The destroy() method is called before the applet is unloaded completely. It is called after the stop() method. Users may reload the applet later, but if they do it will be as if they've never seen it before. All variables, static, member, local or otherwise will be initialized to their initial state. If you have any final cleanup to do (for instance sending output back to the httpd server) do it here.
     public void destroy() {
        theList.addItem("destroy event");
      }
    - update
    The update() method is called automatically by the system. It's often overridden when you want to use offscreen Images to avoid flicker.
     public void update(Graphics g) {
        theList.addItem("update event");
      }
    - mouseUp
    The mouseUp() method is called whenever the mouse button is released in your applet. In most cases this is the event you'll want to watch out 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
    The 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
    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;
      }
    - mouseMove
    mouseMove methods occur when a user moves the mouse without holding down the mouse button. mouseMove methods receive the coordinates of the point where the mouse is when the event occurs.
     public boolean mouseMove(Event e, int x, int y) {
        theList.addItem("mouseMove event at (" + x + "," + y + ")");
        return false;
      }
    - mouseEnter
    Your applet receives a mouseEnter event whenever the cursor enters your applet from somewhere else. You'll also receive the coordinates of the point at which the cursor entered your applet. After this happens its typically followed by a Stream of mouseMoved events as the cursor continues through the applet so it can be hard to see.
     public boolean mouseEnter(Event e, int x, int y) {
        theList.addItem("mouseEnter event at " + x + "," + y + ")");
        return false;
      }
    - mouseExit
    Your applet receives a mouseExit event whenever the cursor leaves your applet. You'll also receive the coordinates of the point at which the cursor exited your applet.
    public boolean mouseExit(Event e, int x, int y) {
        theList.addItem("mouseExit event at (" + x + "," + y + ")");
        return false;
      }
    - getFocus
     public void getFocus() {
        theList.addItem("getFocus event");
      }
    - gotFocus
    public void gotFocus() { theList.addItem("gotFocus event"); }
    - lostFocus
     public void lostFocus() {
        theList.addItem("lostFocus event");
      }
    - keyDown
    A keydown event is generated whenever the user presses a key while your applet is active. An integer keycode is returned indicating which key was pressed. As a general rule you'll want to cast this to a char to get the actual letter.
     public boolean keyDown(Event e, int x) {
        theList.addItem("The " + (char) x + " key was pressed.");
        return false;
      }

    4.6 - Drawing Rectangles

  • Next we'll write an applet that fills the screen with lots of randomly sized and positioned rectangles in the style of Piet Mondrian. We'll do this by parts.

  • 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. Compile it and run the applet.
    //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-1, height-1);
      }
    }
    In Java the coordinate system for an applet begins in the upper left hand corner and increases to the right and down. The upper left hand corner of the applet starts at (0, 0), not at (1, 1). That is the reason for using width-1 and height-1 in g.drawRect. 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).
  •  If we want to draw a filled rectangle we use the fillRect method. Otherwise the syntax is identical.  Here's the code:

  • //Draw a rectangle
    import java.applet.*;
    import java.awt.*;
    public class Mondrian2 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);
      }
    }
  • 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.
    Here is the whole code.
    //Draw a rectangle
    import java.applet.Applet;
    import java.awt.*;
    public class Mondrian3 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 Mondrian4 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:
  • If these aren't sufficient for your needs, you can define others using the same RGB triple that's used to set background colors on many web pages. You even get to use decimal numbers instead of the hex values you have to use for the bgcolor tag. For example to select a medium gray you'd use 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 Mondrian5 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 Mondrian6 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 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. In this case the code above would have to be modified to include the lines  just before the repaint(); call

  •    String s = getParameter("Number");
        if (s != null) {
          numberRectangles = Integer.valueOf(s).intValue();
      }

    4.7 - Drawing Lines

  • The awt contains several graphics primitives. Rectangles are one. Lines are another. Within a graphics context there is one key line drawing method, drawLine(int x1, int y1, int x2, int y2). This method draws a straight line between the point (x1, y1) and the point (x2, y2). Ex: Here's a simple applet that draws a line diagonally across the applet frame:

  • 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);
     }
    }

    4.8 - Graphing Functions

  • We're now going to demonstrate the use of the drawLine() method to draw considerably non-straight figures. 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 for loop in the paint method loops 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. One has other problems:
    1. Sines are floating point operations. To do a really useful graphing applet we need to be able to use floating point numbers.
    2. The coordinate system of an applet counts from (0,0) at the upper left hand corner to the right and down. The standard Cartesian coordinate system we expect graphs to use counts from (0,0) in the lower left hand corner to the right and up. The origin can be moved in both systems, for instance to the center of the applet, but we still need to transform between the y down and the y up coordinates.
    What we need is to separate the data from the display. 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?  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!
     

    4.9 - Defining a function at runtime

     4.9.1 - An infinite set with zero length

  • We're now going to use Java to implement some classic examples of fractal geometry.
  • We begin with a one-dimensional set with an infinite number of points that covers zero length. 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. This 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;
    // The Vector class implements a growable array of objects. Like an array,
    //  it contains components that can be accessed using an integer index.
    Vector endpoints = new Vector();
      public void init() {
        Dimension d = size();
        AppletHeight = d.height;
        AppletWidth = d.width;
            // addElement adds the specified component to the end of this vector,
        // increasing its size by one.
        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
            // size() returns the number of components in this vector.
         for (int j=0; j < endpoints.size(); j += 2) {
               // elementAt(j) returns the component at the specified index.
           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);
     }
    }
    The code above 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.

     4.9.2 - Flying Lines

  • Compile the following code, run it and then look over the code to see if you can understand the algorithm.

  • //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

    4.10 - Taking Action: Threads in Applets

    Depending on your operating system and Java-enabled browser you may have noticed that the Mondrian and Flying Line programs tended to hog your CPU.  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. 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.  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) {
           }
        } // the VM doesn't want us to sleep anymore,
      }   // so get back to work
      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 added four key things to Mondrian to make it threaded and a lot more CPU friendly.
  • After the line kicker = new Thread(this); has been executed the ThreadedMondrian is in a New Thread state. When a thread is a New Thread, it is merely an empty Thread object; no system resources have been allocated for it yet. When a thread is in this state, you can only start the thread. The start method creates the system resources necessary to run the thread, schedules the thread to run, and calls the thread's run method.
  • After the start method has returned, the thread is "running". Yet, many computers have a single processor, thus making it impossible to run all "running" threads at the same time. The Java runtime system must implement a scheduling scheme that shares the processor between all "running" threads.  So at any given time, a "running" thread actually may be waiting for its turn in the CPU.  Execution of multiple threads on a single CPU, in some order, is called scheduling. The Java runtime supports a very simple, deterministic scheduling algorithm known as fixed priority scheduling. This algorithm schedules threads based on their priority relative to other runnable threads.
  • When a Java thread is created, it inherits its priority from the thread that created it. You can also modify a thread's priority at any time after its creation using the setPriority method. Thread priorities are integers ranging between MIN_PRIORITY and MAX_PRIORITY (constants defined in the Thread class). The higher the integer, the higher the priority. At any given time, when multiple threads are ready to be executed, the runtime system chooses the runnable thread with the highest priority for execution. Only when that thread stops, yields, or becomes not runnable for some reason will a lower priority thread start executing. If two threads of the same priority are waiting for the CPU, the scheduler chooses one of them to run in a round-robin fashion.
  •  ThreadedMondrian's run method runs forever (while(true) loop) until some action is taken and the applet is stopped. Within the loop, the applet repaints itself and then tells the thread to sleep for one second (1000 milliseconds). During the second that the ThreadedMondrian is asleep, the thread does not run, even if the processor becomes available. After the second has elapsed, the thread becomes Runnable again and, if the processor becomes available, the thread begins running again. An applet's repaint method ultimately calls the applet's paint method, which does the actual update of the applet's display area.
  • The ThreadedMondrian paint method draws  a rectangle around the applet and also draws random colored rectangles inside the applet area.
  • A program doesn't stop a thread like it stops an applet (by calling a method). Rather, a thread arranges for its own death by having a run method that terminates naturally. The exit condition for this run method is the exit condition for the while loop because there is no code after the while loop while (true) {. This condition indicates that the loop will exit when the currently executing thread finds out that true is not true . When would this ever be the case?
  • When you leave the page, the application in which the applet is running calls the applet's stop method. This method then sets the ThreadedMondrian to null, thereby telling the main loop in the run method to terminate.
  • If you revisit the page, the start method is called again and ThreadedMondrian starts up again with a new thread.
  • Here's a threaded version of Flying Lines.

  • //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

    4.11 - Interaction: Mouse and Keyboard Input

  • Here's a simple applet that lets you doodle with the mouse on an applet.

  • 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;
      }
    }

    4.12 - Keyboard Input: TypeWriter

  • Here's a simple applet that uses the keyDown method to let you type some text.

  • 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));
        }
      }
    }

    4.13 - The Mandelbrot Set

  • The Mandelbrot set is a classic application of complex arithmetic. It is the example of a fractal set. 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 don't 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;
          }
        }
      }
    }
  • To make this interesting we want to actually draw pictures of the Mandelbrot Set. To do this we'll move the actual calculation into a thread in an applet and then draw the results into a bitmap. Here's the code:

  • 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.

    Acknowledgments:

    These Notes have benefited from materials adapted from several sources, e.g.,
    1. The Java Tutorial, downloadable from http://java.sun.com
    2. Thinking in Java, by Bruce Eckel, downloadable from www.BruceEckel.com
    3. Brewing in Java: A Tutorial, by Elliotte Rusty Harold, downloadable from http://metalab.unc.edu/javafaq/javatutorial.html
    4. The Java Language Environment: A White Paper, by James Gosling & Henry McGilton, downloadable from http://java.sun.com/docs/white/langenv/