This is a beginners' review of Java intended for people that are already programmers, our table of contents and basic reference material for the QuizSite Java exercises to be posted later this week.

1. Abstraction, Encapsulation, Inheritance, and Polymorphism

Abstraction is the process of refining away the unimportant details of an object, so that only the appropriate characteristics that describe it remain. These, together with the operations on the data, form an abstract data type (ADT). Abstraction is where program design starts. All mainstream programming languages provide a way to store pieces of related data together. It is usually called something like a structure or a record.

Encapsulation means grouping together the types, data, and functions that make up a class, and providing some information hiding to control access. In C, for example, a header file provides a very weak example of encapsulation, because it is a purely lexical convention, and the compiler knows nothing about the header file as a semantic unit.

Non-object-oriented programming languages support encapsulation very well for built-in types, and not at all for user-defined types. Take, for example, floating point numbers: in most languages the only valid thing you can do with them is arithmetic operations and I/O. Even in C, if you try to shift left the bits of a floating point number the compiler will print out an error message. The valid operations for a float are encapsulated (bundled together) as part of the type information, and shifting isn't one of them. The compiler enforces the rule that "the operation must be compatible with the type".

Object-oriented programming languages extend the support for encapsulation (bundling together types and the functions that operate on those types, and restricting access to the internal representation) to also cover user-defined types. They enforce the integrity of a type by preventing programmers access individual fields in inappropriate ways. Only a predetermined group of functions can access the data fields.

The collective term for "datatype and operations bundled together, with access restrictions" is a class. The individual elements in a class are fields. A variable of some class type is called and instance (or an object). It's pretty much the same as a variable of an ordinary type except the valid functions that can be invoked for it are tied to it.

Some class terminology

Constructors and Finalizers A constructor is a special kind of method that initializes a newly-created object. There are no destructors in Java (in the C++ sense) because the memory management is automatic. You can provide a finalizer for a class, and it will be invoked on an object right before it is garbage collected.

Inheritance means being able to declare a type which builds on the fields (data and methods) of a previously-declared type. As well as inheriting all the operations and data you get a chance to declare your own versions and new versions of the methods, to refine, specialize, replace or extend the ones in the parent class.

Casting Casting is the C term for type conversion, carried over into Java.

Inheritance and Constructors

  1. A superclass constructor is always invoked when you create a subclass object.
  2. Sibling constructors can call each other with the this reference.

Abstract and Final

abstract and final are somewhat opposites of each other:

One cannot create (instantiate) an object on an abstract class.

Multiple inheritance per se does not exist in Java, but it can be simulated with interfaces (as we shall review later).

Visibility and Other Name Modifiers

Class Modifiers
modifier class name 
         [extends name] 
         [implements namelist] 
  body 
The modifier can be one or more of:
  1. abstract - class must be extended to be useful
  2. final - class cannot be extended
  3. public - class is visible in other packages
abstract and final are opposites and are not allowed together.
The fields in a class body can be variables, or methods.

The field can similarly start with a modifier that says how visible it is, and qualifies it in some other way.

Data Field Modifiers
Visibility
public field is visible everywhere (class must be public too)
<blank> what you get by default. Field is only visible in this package.
protected like default, and the field is visible in subclasses in other packages extended from this class, too
private field is only visible in this class
Behaviour
static one per class, not one for each object
final cannot change value
transient a hint used in object serialization
volatile this data may be written to by several threads of control, so the run time system has to take care to always get the freshest value when reading it

Method Field Modifiers
Visibility
public field is visible everywhere (the class must be public too)
<blank> the default (field is visible in this package)
protected like default, and the field is visible in subclasses in other packages extended from this class
private field is only visible in this class
Behaviour
final cannot be overridden
static one per class, not one for each object
abstract must be overridden (to be useful)
native not written in Java (no body, but otherwise normal, and can be inherited, static, etc.) The body will be written in another language.
synchronized only one thread may execute in this method at a time. Entry to the method is protected by a monitor lock around it.

Static There are three varieties of a static thing (once only) in Java. A static thing is something in a class that occurs once only, instead of once for each instance of the class.

  1. static data: class variable.

  2. static methods: class methods

  3. static blocks: a portion of executable code belonging to the class, commonly used for initialization.

Polymorphism means reusing the same name for a related concept on different types. The trivial kind of polymorphism is called overloading and it means that in any class you can use the same name for several different (but hopefully related) methods. The second, more complicated kind of polymorphism (true polymorhism), is resolved dynamically (at run time). It occurs when a subclass has a method with the same name and signature (that is, number, type and order of parameters) as a method in the superclass. When this happens, the method in the derived class overrides the method in the superclass.

Example

class Fruit {
  int  grams; 
  int  cals_per_gram; 
  int  total_calories() { /* ... */ } 
  void peel() { System.out.println("Peel a Fruit"); } 
} 

class Citrus extends Fruit {
  void squeeze() { /* ... */ } 
  void peel() { System.out.println("Peel a Citrus"); } 
}

class Example {
  public static void main (String args[]) {
    Fruit somefruit = new Fruit(); 
    Citrus lemon = new Citrus(); 
    somefruit.peel();      // [1] Fruit
    lemon.peel(); 
    somefruit = lemon;
    somefruit.peel();      // [2] Citrus 
    } 
} 
Terminology
class
a (user-defined) data type

extend
to make a new class that inherits the contents of an existing class, the class that it extends

superclass
a parent (or "base") class

subclass
a child class that inherits, or extends, a superclass

Concept Java term C++ term
function in class method member function (member method)
class that is expected to be extended abstract virtual
anything in class field member
parent relationship with another class extend inherit
class you extend superclass base class
extended class subclass derived class
initialization method constructor constructor
finalization method finalizer (in part) destructor

2. The Language The Anatomy of an Application

class pentium {
  public static void main(String args[]) { 
    double x, y, z; 
    x = 4195835.0; y = 3145727.0; 
    z = x - (x / y) * y; 
    System.out.println("results = " + z); 
  } 
}
As in C the signature of main() is magic and it tells the runtime system to start execution here (with it). In Java main() is defined to return a void (no value). In C it is defined to return an int.

The modifiers say that this function is visible everywhere (public) and it is a method for the class as a whole, not specific to individual objects (static).

The rest of the code is self-explanatory.

Identifiers, Comments, Keywords, Operators

Identifiers can be any length, and they must start with a letter, underscore, or dollar sign, and in subsequent positions can also contain digits. Java has been designed with internationalization in mind, and it uses the 16-bit Unicode character set.

Commenting out code can be done in several ways:

  1. // to the end of line
  2. /* anything in between */
  3. something like this
    if (false) {
      what needs to be skipped 
    }
Keywords are reserved words and may not be overloaded for use as identifiers. The keywords can be divided into several categories according to their main use:
  1. Used for built-in types
    boolean  true    false
    char 
    byte     short   int    long
    float    double
    void

  2. Used in expressions
    null  new  this  super

  3. Used in statements
    selection statements
    if     else
    switch case break default 

    iteration statements
    for  continue
    do   while

    transfer of control statements
    return 
    throw

    guarding statements (threads or exceptions)
    synchronized
    try catch finally 

  4. Used to modify declarations (scope, visibility, sharing, etc.)
    static
    abstract   final 
    private    protected   public
    transient  volatile

  5. Used for other method or class-related purposes
    class instanceof throws native

  6. Used for larger than class building blocks
    extends 
    interface  implements 
    package    import

  7. Reserved (but not in use)
    cast   const     future   generic   goto
    inner  operator  outer    rest      var 

Operators

A C or C++ programmer will be readily familiar with the operators in Java. The novel aspect is that the order of evaluation is well-defined.

The Java order of evaluation says that for all binary (two argument) operators the left operand is always fully evaluated before the right operand.

There are three factors that influence the ultimate value of an expression in any algorithmic language, and they work in this order:

  1. precedence
    Precedence says that some operators bind more tightly than others.
  2. associativity
    Associativity is the tie-breaker for deciding the binding when we have several operators of equal precedence strung together. This only groups operands, does not say anything about the order in which they are evaluated.
  3. order of evaluation
    The order of evaluation (if it is specified in a language) tells us the sequence, for each operator, in which the operands are evaluated. In java the order of evaluation is left to right, so after
    (i=2)*i++
    i is 3 after the expression, which evaluates to 4 (the left operand to the multiplication will be evaluated before the right operand, then the multiplication will be done, and then i will be incremented).

    That is to say that i will be set to 2 first and 2 will be the value for the first operand in the multiplication. Then i++ will be evaluated for the second operand. This makes i 3 and 3 is the value returned for the second operand to *. The final result is 6, with i set to 3.

Symbol Note Prec Assoc
names, literals simple tokens 16 n/a
a[i] subscripting 16 left
m( ... ) method invocation 16 left
. field selection 16 left
++ -- increment, decrement 16 right
++ -- increment, decrement 15 left
~ flip the bits of an integer 14 right
! logical not (reverse a boolean) 14 right
- + arithmetic negation, plus 14 right
(typename) type conversion (cast) 13 right
* / % multiplicative operators 12 left
- + additive operators 11 left
<< >> >>> left and right bitwise shift 10 left
instanceof < <= > >= relational operators 9 left
== != equality operators 8 left
& bitwise and 7 left
^ bitwise exclusive or 6 left
| bitwise inclusive or 5 left
&& conditional and 4 left
|| conditional or 3 left
? : conditional operator 2 right
= *= /= &= += -= <<= >>= >>>= &= ^= |= assignment operators 1 right

3. Building Blocks

Primitive Types

There are 8 (eight) built-in, non-object types, also known as primitive types. They are discussed below:

  1. boolean
    For truth values. Literals are: true and false.
  2. int, long, byte, short (for arithmetic on integers)
    intis a 32-bit, signed, two's complement number (range of values is -232 to (232 - 1)). long is 64 bits, byte is 8 bits, and short is 16 bits long.
  3. double, float (for arithmetic on the real numbers)
    double refers to floating-point numbered stored in 64 bits. float is on 32 bits.
  4. char (for character data, ultimately to be input or printed)
    16-bit unsigned quantity that is used to represent printable characters.

    Range of values: a value in the Unicode code set 0 to 65,535.

String is not a primitive type like the types described up to now in this chapter. On the contrary, the type String is a type in Java, but it is important enough to have support for it built into the language. Here's the interface for String for your reference and information (taken from Flanagan):
public final class String extends Object implements Serializable {
   // Public Constructors
      public String();
      public String(String value);
      public String(char[] value);
      public String(char[] value, int offset, int count);
 #    public String(byte[] ascii, int hibyte, int offset, int count);
 #    public String(byte[] ascii, int hibyte);
 1.1  public String(byte[] bytes, int offset, int length, String enc) 
                   throws UnsupportedEncodingException; 
 1.1  public String(byte[] bytes, String enc) 
                   throws UnsupportedEncodingException;
 1.1  public String(byte[] bytes, int offset, int length);
 1.1  public String(byte[] bytes);
      public String(StringBuffer buffer);
   // Class Methods ------------------------------------------------------
      public static String copyValueOf(char[] data, int offset, int count);
      public static String copyValueOf(char[] data);
      public static String valueOf(Object obj);
      public static String valueOf(char[] data);
      public static String valueOf(char[] data, int offset, int count);
      public static String valueOf(boolean b);
      public static String valueOf(char c);
      public static String valueOf(int i);
      public static String valueOf(long l);
      public static String valueOf(float f);
      public static String valueOf(double d);
   // Public Instance Methods
      public char charAt(int index);
      public int compareTo(String anotherString);
      public String concat(String str);
      public boolean endsWith(String suffix);
      public boolean equals(Object anObject);  // Overrides Object.equals()
      public boolean equalsIgnoreCase(String anotherString);
 #    public void getBytes(int srcBegin, int srcEnd, byte[] dst, int dstBegin);
 1.1  public byte[] getBytes(String enc) throws UnsupportedEncodingException;
 1.1  public byte[] getBytes();
      public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin);
      public int hashCode();  // Overrides Object.hashCode()
      public int indexOf(int ch);
      public int indexOf(int ch, int fromIndex);
      public int indexOf(String str);
      public int indexOf(String str, int fromIndex);
      public native String intern();
      public int lastIndexOf(int ch);
      public int lastIndexOf(int ch, int fromIndex);
      public int lastIndexOf(String str);
      public int lastIndexOf(String str, int fromIndex);
      public int length();
      public boolean regionMatches(int toffset, String other, 
                                   int ooffset, int len);
      public boolean regionMatches(boolean ignoreCase, int toffset, 
                                   String other, int ooffset, int len);
      public String replace(char oldChar, char newChar);
      public boolean startsWith(String prefix, int toffset);
      public boolean startsWith(String prefix);
      public String substring(int beginIndex);
      public String substring(int beginIndex, int endIndex);
      public char[] toCharArray();
 1.1  public String toLowerCase(Locale locale);
      public String toLowerCase();
      public String toString();  // Overrides Object.toString()
 1.1  public String toUpperCase(Locale locale);
      public String toUpperCase();
      public String trim();
}

The Basic Statements

All statements in Java can be conveniently divided into four groups:

  1. Selection statements

    if ( Expression ) 
      Statement 
    [ else Statement ]
    


    switch ( Expression ) {
      case constant_1: Statement; break 
      case constant_2: Statement; break 
          ...
      case constant_n: Statement; break 
              default: Statement; break 
    }

  2. Iteration statements

    for ( Initial ; Test ; Increment ) 
      Statement

    while ( Expression ) 
      Statement

    do Statement 
    while ( Expression )

    continue; 
    
    continue Identifier; 
    Here's an example:
    months: for (int m=1; m <= 12; m++) {
      // do something 
      // nested loop 
      for (int d=1; d <= 31; d++) {
        // some daily thing 
        if (m == 2 && d == 28) continue months; 
        // otherwise something else 
      } 
    } 

    break;
    
    break Identifier; 
    Example:
    months: for (int m=1; m <= 12; m++) {
      // do something 
      // nested loop 
      for (int d=1; d <= 31; d++) {
        // some daily thing 
        if (m == 2 && d == 28) break months; 
        // otherwise something else 
      } 
    } 
    cost = 0;  

  3. Transfer of control statements

    return
    
    return Expression; 
    

  4. Guarding statements

    synchronize
The selection, iteration, and branching statements are almost identical to their counterparts in C, and are readily recognizible to any programmer familiar with mainstream algorithmic languages.

The fourth one, are best described in the context of threads.

Objects

Java does not have pointers as they exist in C or C++. Pointers in Java are called references. You can define linked lists in Java easily as we have seen in hw #7.

The Object class and its methods.

public native int hashCode()
A hascode is an identifying value, ideally unique for every object in a program. A hashcode could be implemented as anything but in current compilers it looks a lot like an address. This and the next method go together.
public boolean equals(Object obj)
There are two philosophies for equality comparison -- something may have the same reference as what you're comparing it to ("is it at the same address?"), or it may have the same value ("are these bits here the same as those bits there?"). The Object version of this method returns true when the two objects being compared are actually the same one object. This is also the precise comparison of the == operator. However, you can override equals with your own version, to give it a weaker definition. This has been done for String because two strings are equal when they have the same characters. When you override equals you need to overridehashCode too, make certain that equal Objects get the same has codfe.
protected void finalize() throws Throwable
The method finalize() is just a hook you can override to help with memory management.
protected native Object clone()
                        throws CloneNotSupportedException
Java supports the notion of cloning, meaning to get a complete bit-for-bit copy of an object. There are two kinds of cloning: shallow and deep.
public final native Class getClass()
Java maintains a run time representation for all classes (known as RTTI, for "Run Time Type Identification"). This method gets that information (which is of type Class, see below)
The Class class.
The run time representation (or RTTI, for "Run Time Type Identification") is stored in objects whose class is called Class. The major use of Class is to help with dynamically loading new classes. This is a systems programming activity unlikely to occur in your programs.
Other built-in classes

Basic type      Corresponding class in java.lang

  boolean          Boolean
  char             Character 
  byte             Byte
  short            Short 
  int              Integer 
  long             Long 
  float            Float 
  double           Double

Compilation Units

A source code is what you present to the Java compiler so that the contents of a complete source file are known as a "compilation unit".

An important aspect is that Java has a make-like utility built into it.

File Name is Related to Class Name

Java emphasizes the idea that the name of a file is related to its contents, and understanding this is the key to understanding how to build programs out of more than one file or library. A compilation unit (or source file) has several rules:

  1. It can start with a package statement, identifying the package (a library) that the bytecodes will belong to. When starting out it is simplest not to use packages. Your class files will be visible to other classes in the same directory and belong to a default anonymous package.
  2. Next can come zero or more import statements, each identifying a package or a class from a package that will be available in this compilation unit.
  3. The rest of the compilation unit consists of class declarations and interface declarations. Interfaces are skeletons of classes showing what form the class will take when someone implements it.
  4. At most one of the classes in the file can be public. This class must have the corresponding name as the file it is in.

The underlying host system should allow filenames with the same form as Java classnames.

At compilation time each of the classes in a .java file will create an individual .class file to hold the generated byte codes.

Packages

The Java term package means a collection of individual .class files in a directory. So a package is both a directory and a library.

The CLASSPATH Variable
Is an environment variable, typically set in the autoexec.bat file or in a shell initialization file.

Import

You never have to use the import statement. Its effect is to allow you to use a class name directly, instead of fully qualifying it with the package name.

Visibility Issues

Four examples illustrating the interaction of public, protected, default (package), and private. We'll skip those for now, maybe I will present them later in separate notes, referring exclusively to packages. Interfaces

In languages with multiple inheritance names of methods sometime collide. For example if you had a

class FlyingMachine
and a
class FloatingVessel
and both have a navigate method inside, what is
class Seaplane extends FlyingMachine, FloatingVessel
going to inherit (if multiple inheritance were available).

Which navigate method,

In Java there is no multiple inheritance and interfaces avoid the kind of problem discussed above. An interface is a skeleton of a class, showing the methods the class will have when someone implements it. An interface may look like this:
interface FlyingMachine {
   int navigate(Point from, Point to);
  void land(); 
  void takeoff(double fuel); 
}
(The declarations in an interface are always public even if you don't label them so.) An interface is thus a way of saying "you need to plug some code in here for this thing to fully work". A later class implements the interface by defining those methods including their bodies. If two interfaces should happen to demand a method of the same name in a class it isn't a problem. The implementation will define it(s meaning).
interface FloatingVessel {
   int navigate(Point from, Point to); 
  void dropAnchor(); 
  void WeighAnchor(); 
}

class SeaPlabe implements FloatingVessel, FlyingMachine {
  // this is where we define the methods
  // void dropAnchor()
  // void weighAnchor()
  // int  navigate(Point from, Point to)
  // void land()
  // void takeoff(double fuel) 
} 

An interface differs from an abstract class in the following ways:

  1. a class can implement several interfaces at once, whereas a class can only extend one parent class
  2. an interface doesn't have any overtones of specialization that are present with inheritance. It just requires an implementation for a set of methods.
  3. Interfaces can be used to support callbacks (inheritance doesn't help with this)
Using interfaces dynamically. (Callbacks).

Once we have defined an interface, we can use it as the type of some parameter to a method. Inside the method we can use that parameter to invoke the operations that the interface promises. Whenever we make a call to that method, we can pass it a class that implements that interface, and it will call the approproate method of that class. The following four steps should make this a bit more clear:

  1. Define an interface that promises a method called run like this:
    interface runnable {
      public void run(); 
    }
  2. Now sprinkle calls to run throughout your code, even though we don't yet have anything that fulfills the interface. This is one such place:
    void vitalSystemThing(runnable r) {
      r.run(); 
    }
    This code can be compiled and put in a library.
  3. At a later time and in another file altogether we provide a class (or several classes) that implements the defined interface:
    class myCode implements runnable {
      public void run() {
        System.out.println("You called myCode.run()"); 
      }
      // ... other code 
    }
  4. Finally, we pass myCode to the vitalSystemThing:
    vitalSystemThing(myCode)
    This results in myCode.run() being called back, hence this is known as a "callback".

    We have described this when we looked at RMI (clients were able to register with a server and allowing it to notify them of subsequent developments).

    This is a longwinded way of doing what you could do with a function pointer in C.

    Note: the main reason for writing your code this way is that it decouples the calling routine from the called back routine. You could have several different classes that implement the runnable interface. Callbacks allow any one of them to be sent to the vitalSystemThing and hence called back. The correct class is called back based on the type at runtime.

4. Sample Techniques

Arrays

Java has neither the complexities nor the "size fixed at compile time" of C arrays. The Java array model is more like that of Ada: arrays of arbitrary bounds as parameters and dynamically-allocatable arrays each carrying around information about its own length.

In some sense arrays are like objects:

  1. the language says so
  2. they are a reference type
  3. they are always allocated on the heap not on the stack
  4. inherit from Object and support methods such as toString()
  5. are allocated with the new operator

On the other hand arrays are not like objects in the following ways:

  1. they have a different syntax from other object classes
  2. you can't subclass or extend arrays
  3. you can't define your own methods for arrays
  4. an array can't implement an interface or be cloned
Examples:
int carrot[]; 

int carrot[] = new int[256]; 

a.legth // just a final data field so to speak 
Arrays of Arrays

Examples:

int cabbage[][];
or, create a triangular array of arrays and fill it with initial values:
int cabbage[][] = new int[5][];

int slice0[] = {0}; 
int slice1[] = {0, 1}; 
int slice2[] = {0, 1, 2}; 
int slice3[] = {0, 1, 2, 3}; 
int slice4[] = {0, 1, 2, 3, 4}; 

cabbage[0] = slice0;
cabbage[1] = slice1;
cabbage[2] = slice2;
cabbage[3] = slice3;
cabbage[4] = slice4;
that could be done in the following way too:
int cabbage[][] = { {0}, 
                    {0, 1},
                    {0, 1, 2},
                    {0, 1, 2, 3},
                    {0, 1, 2, 3, 4}
                  }; 
Exceptions

Exceptions are for changing the flow of control when some important or unexpected event, usually an error, has occurred. Exceptions are caused in one of two ways: the program does something illegal (usual case) or the program explicitely generates an exception by executing the throw statement (less usual case). Exceptions don't reduce the amount of work you have to do to handle errors, their advantage is that they let you collect it all in well-localized places of your program, so you don't obscure the main flow of control with zillions of checks of return values. The general form of how to catch an exception is
  try block
[ catch (arg) block ]
[ finally              block ]
Summary of exceptions:
Threads
This topic has been thorougly treated in lecture 22
Garbage Collection and Finalizers
Languages with dynamic data structures must have some way of telling the operating system that they need more memory, and conversely their run time system needs to have a way to indicate memory that is no longer in use. It is the job of a special runtime Java component, called the garbage collector to sit on top of the heap and periodically scan it to determine things that aren't being used any more. It can reclaim that memory and put it back in the free store pool within the program.

There is almost no direct interaction between the programmer and the garbage collector system.

A finalizer is a Java term related but not the same as a C++ destructor.

When there are no further references to an object its storage can be reclaimed by the garbage collector. A finalizer is a method from class Object that any class may override. If a class has a finalizer method it will be called on dead instances of that class before the memory occupied by that object is reused.

5. An Example Program. Utilities

Case Study Program

The Sorting Threads Applet by James Gosling

The listed program is using AWT 1.0.2 but is a good example of an applet that uses threads and graphical output to demonstrate the relative speeds of three different, but popular, sorting algorithms.

Utilities

java.util

BitSet
An array of bits indexed by subscript. The collection grows as you need to add more bits.
Date
A class to process day/dates in a system independednt way. Gives access to year, month, day, hour, minute and second.
Enumeration
An interface that specifies two methods to help count through a set fo values.
Hashtable
A very useful class! This class maintains a hash-table that holds object-and-key pairs. This class lets you impose some order and storage capability on arbitrary objects.
Properties
Persistent properties class. Can save a hash table to a stream, and read one back in again. It is an extension of the Hash table class. This is also used for the System properties (used instead of the environment variables, sort of like %ENV in Perl).
Random
Generates pseudo-random numbers in various useful forms and types. Can be completely different each time, or provide the same sequence of random numbers (for testing).
Stack
Implements a last-in/first-out stack of objects.
StringTokenizer
Does simple lexing (separation into individual tokens removing white space) on a text string.

The fundamental idea of StringTokenizer is to instantiate it with a string and it will then break that string upm into islands of characters separated by seas of white space.

In fact it acts like Perl's split so you can specify the delimiters for the substring, and they don't have to be whitespace. One of the constructors allows for new delimiting characters to be set and also takes a boolean saying whether the delimiters themselves should be returned. The representative methods are:

public StringTokenizer (String words_to_breakup); // constructor 
public StringTokenizer (String words_to_breakup
                        String delimiting_chars,
                        boolean return_delims
       ); // also constructor 
public boolean hasMoreTokens(); 
public String nextToken(); 

Vector
Data structure that can hold a collection of arbitrary objects, and provide immediate access by subscript to them. The array will be extended as needed as more objects are added to it.

Examples will be provided in future lab/lecture notes for those that have not been exemplified in past lecture/lab notes.

6. Applets

This part covered in first lecture and in the textbook. Only reminder here with class hierarchy.

Summary of Useful Methods in An Applet

Method Description
void init() Called when the applet is first loaded into memory. Typically you override it with one-time initialization code.
void start() Called each time the browser visits the page containing the applet. Typically you override it to start or resume any threads the applet contains.
void paint(Graphics g) Will be called by the window system when the component needs to be redisplayed. You will not call this. You will override this if you dynamically change the appearance of the screen, and what to see it appear.
void stop() Called when the browser leaves the page containing this applet. Typically you override it to stop or suspend any threads the applet contains.
void run() Nothing to do with applets. This is the main routine in which thread execution starts.

java.applet.Applet 
   |
   +-extends 
     java.awt.Panel
        |
        +-extends 
          java.awt.Container 
             |
             +-extends 
               java.awt.Component -- implements 
                  |                  java.awt.MenuContainer, 
                  |                  java.awt.image.InageObserver, 
                  |                  java.io.Serializable 
                  +-extends 
                    java.lang.Object 


7. I/O and Networking

I/O in Java is highly modular. There are many classes that specialize in one kind of I/O. It is very easy to connect objects of these classes together (like a Unix pipe) so that as the data flows through they are modified or filtered in the way you need.

7.1 Interactive I/O

In an applet the design paradigm is that inside the paint method
public void paint(Graphics g)
one can draw a String by calling a routine that operates on the Graphics object argument to paint:
g.drawString(some_string, x, y)

To input some text in an applet one would use a text widget (text area or text field). To read values from the keyboard in applications we use file i/o, as summarized below:

Preliminary Declarations
import java.util.*;
import java.io.*;

static DataInputStream dis = new DataInputStream (System.in); 
static StringTokenizer st;
Note: an exception handler declaration is also needed.
Type Code
boolean
st = new StringTokenizer(dis.readLine()); 
boolean bo = new Boolean(st.nextToken()).booleanValue();
char
char ch=dis.readLine().charAt(0);
int
st = new StringTokenizer(dis.readLine()); 
int myint = Integer.parseInt(st.nextToken()); 
byte input as int and cast to byte
short input as int and cast to short
long
st=new StringTokenizer(dis.readLine()); 
long mylong = Long.parseLong(st.nextToken()); 
float
st = new StringTokenizer(dis.readLine()); 
float f = new Float(st.nextToken()).floatValue();
double
st = new StringTokenizer(dis.readLine()); 
double d = new Double(st.nextToken()).doubleValue();
String
String s = dis.readLine(); 
Notes:
try {
  // put your I/O statements here 
} catch (IOException ioe) {
  ioe.printStackTrace(); 
}
Each statement or group of statements must have an exception handler wrapped around it. Or, the method must declare that it throws an IOException

We're using methods from the class

DataInputStream
on the
InputStream System.in
object.

File I/O: Streams

Java I/O is built on Streams. A Stream in Java could be getting its bytes from (or sending them to) a file, a String, a socket on the net, an array in memory, the terminal etc. The Stream knows how to pull bytes out of each of these, although the programmer doesn't need to know.

An input stream is something that sends us a succession of bytes, and an output stream is something that we can send a series of bytes to.

A given Stream is device-independent, greatly reducing the range of library calls one need to memorize. For example in case of networking problems one could temporarily modify the socket stream to receive data from an in-memory array and then change it back when we have everything debugged.

Input Streams

  1. FileInputStream

    Gets bytes from a file.

  2. StringBufferInputStream

    Gets bytes from a StringBuffer.

  3. ByteArrayInputStream

    Gets bytes from an array.

  4. PipedInputStream

    Gets bytes from a pipe (a data structure that squirts bytes at you).

More detailed examples will have to be deferred.

Output Streams

  1. FileOutputStream

    Writes bytes to a File.

  2. ByteArrayOutputStream

    Puts bytes into its own temporary internal storage, for later retrieval.

  3. PipedOutputStream

    Puts bytes into a pipe (just a data structure that drinks down bytes that you pour into it).

Layering Streams on Top of One Another

You can connect one Stream to another and the results is also a Stream. Each Stream in this chain has the chance to modify the data as it goes by. Streams can be layered on top of each other to provide filtered processing.

Filtered Input Streams The constructors of these filtered streams take a Stream as an argument and give back a filtered stream. Java provides the following filtered input streams:

FilteredOutputStreams

The constructors of these filtered streams take a Stream as an argument and give back a filtered stream. Java provides the following filtered output streams:

I/O Exceptions

Tha basic I/O related exceptions are:

  1. FileNotFoundException
  2. InterruptedIOException
  3. UTFDataFormatError
  4. EOFException
Reviewing the Interactive Input Routines

Random Access

The class RandomAccessFile allows the file pointer to be moved to arbitrary positions in the file prior to reading or writing. The File class.

A number of methods are available in the class File to provide information about a file or a directory:

Platform specific I/O (separators)

7.2 Networking with Java

Networking at heart is all about shifting bits from point A to point B. Usually we bundle the data bits into a packet with some more bits that say where they are to go. That, in a nutshell, is Internet Protocol, or IP. If we want to send more bits than will fit into a single packet, we can divide the bits into groups and send them in several successive packets. These are called User Datagrams.

User Datagrams can be sent across the Internet using UDP (the User Datagram Protocol), which relies on the Internet Protocol for addressing and routing. UDP is like going to the post office, sticking on a stamp, and dropping off the packet. IP is what the mail carrier does to route and deliver the packet. UDP is used by SNMP (Simple Network Management Protocol) and TFTP (Trivial File Transfer Protocol).

Just as when we send several pieces of postal mail to the same address, the packages might arrive in any order. Some of them might even be delayed, or even on occasion lost altogether. This is true for UDP too; you wave good-bye to the bits as they leave your workstation, and you have no idea when they will arrive where you sent them, or even if they did.

Uncertain delivery is equally undesirable for postal mail and for network bit streams. We deal with the problem in the postal mail world (when the importance warrants the cost) by paying an extra fee to register the mail and have the mail carrier collect and bring back a signature acknowledging delivery. A similar protocol is used in the network work to guarantee reliable delivery in the order in which the packets were sent. This protocol is known as Transmission Control Protocol or TCP. Two applications that run on top of, or use, TCP are: FTP, the File Transfer Protocol, and telnet.

TCP uses IP as its underlying protocol (just as UDP does) for routing and delivering the bits to the correct address. However, TCP is more like a phone call than a registered mail delivery, in that a real end-to-end connection is held open for the duration of the transmission session. It takes a while to set up this stream connection, and it costs more to assure reliable sequenced delivery, but often the cost is justified.

The access device at each end-point of a phone conversation is a telephone. The access object at each end-point of a TCP/IP session is a socket. Sockets started life as a way for two processors on the same Unix system to tslk to each other, but some smart programmers realized that they could be generalized into connection end-points between processes on different machines connected by a TCP/IP network.

Sockets can deliver fast and dirty using UDP (this is a datagram socket), or slower, fussier, and reliably using TCP (this is termed a stream socket). Sockets connections generally have a client end and a server end. Generally the server end just keeps listening for incoming requests ("operators are standing by" kind of thing). The client end initiates a connection, and then passes or requests information from the server.

What's in the Networking Library

InetAddress The class that represents IP addreses and the operations on them.
DatagramPacket A class that represents a datagram packet containing packet data, packet length, internet addresses and port. Packets can be sent and received.
DatagramSocket This class allows datagrams to be sent and received using the UDP.
PlainSocketImpl This is the default socket implementation. It doesn't implement any security checks nor support firewals.
SocketImpl This is the Socket implementation class. It is an abstract class that must be subclassed to provide an actual implementation. The Unix Socket API does not distinguish between client and server sockets, but the Java API does.
ServerSocket The server Socket class. It uses a SocketImpl to implement the actual socket operations. It is done this way so that you can change socket implementations depending on the kind of firewall used.
Socket The client Socket class. It uses a SocketImpl to implement the actual socket operations. Agai, this permits you to change socket implementations depending on the kind of firewall that is used.
URL The class represents a Uniform Reference Locator - a reference to an object on the web. You can open an URL and retrieve the contents, or write to it.

TCP/IP Client Server Model

Here's how a client and a server typically communicate over a TCP/IP network connection.

The processes contact each other by knowing the IP address and a port number. The IP address is like a telephone number, and a port number is like an extension at that number. Together they specify a unique destination. The client and server must agree on the same port number (just as for telephone calls).

For simplicity network socket connections are made to look like streams. You simply read and write data using the usual stream methods and it automagically appears at the other end. There's a method to get the input stream of a socket and a method to get the output stream. This allows the client and server to talk back and forth.

8. Utilities and Libraries Tentative Overview

9. The AWT

Continues the portability goals of the language. Provides a single-windowing user interface ojn systems with wildly different native window systems. AWT 1.0.2 is covered here.

The Role of Objects

Method inheritance is a key part of the window interface in Java. The basic components of a GUI (buttons, textfields, scroll bars, lists and so on) are all created by subclassing the superclass called Component.

9.1 How It Works

The AWT requires the native window system to be running on the platform, as it uses the native window system to implement windows within Java. The basic flow is this:

  1. Java program issues call to add a button to a window on the screen
  2. AWT runtime makes a request to the native window system
  3. ... which puts the button on the screen.
  4. Later on the button is pressed by mouse,
  5. the native window system passes event to Java event handler system,
  6. and from there into Java app code, which processes it.
General Remarks on Window Systems

Windowing programs are asynchronous. Event-driven programming is used.

In the event-driven programming the program mostly waits for user input. When the user touches something on screen with the mouse pointer the window system catches the event and passes it on to a handler that you earlier supplied. This is known as a callback, and your handler is a "callback routine" because the window system calls back to it when the event happens. Your handler will deal with the graphics event, and the action that is associated with it.

Almost the whole of window programming is learning about the different objects that you can put on the screen (windows, scrollbars, buttons, etc.) Your event handling code for each widget will be invoked as needed.

Details of the AWT

The AWT provides five services to the programmer:

  1. a set of the usual GUI widgets
  2. an event handling system associated with them
  3. the concept of containers (objects that you attach a widget to)
  4. some layout managers (automatically position widgets in containers)
  5. simple support for graphocs operations: lines, arcs, etc.

9.2 The Class Hierarchy

 
Component --+-- lots of widgets (described in section 9.3)
            |
            +-- Container -+- Panel ---- Applet 
                           |
                           +- Window -+- Dialog
                                      |
                                      +- Frame
Components are the most general, kind of GUI object. They do two principal things:
  1. display themselves and all contents on the screen
  2. catch and handle events relating to the widgets, keyboard, and mouse when the focus is on the Component
Containers are of two kind:
  1. subclasses of Window, which are separate pop-up windows in their own right
  2. subclasses of Panel (they generally represent a smaller part/area of a larger Window).
Any Container can be a Component, but not all Components are Containers. Components are more general.
Component
Everything on the screen is built out of Components.
Container
A data structure class that is used to hold several widgets, and treat them as a related group.
Window
This is a totally blank window.
Frame
Specialization of Window on which Menu bars are created and processed.
Dialog
The Dialog class can be used to pop up a window that has an area where the user can type a line of text.
Panel
A container with a built-in way to layout on the screen everything it contains.
Applet
A subclass of Panel.
A Few Words on Getting a GUI Event

(This describes the 1.0 Event model in Java.)

Just as adding widgets to a panel or frame is easy, so is getting back user input. Everything hinges on the method with this signature:

public boolean action(Event e, Object o)
The method is part of every Component so all of the widgets inherit it and can override it.

Whenever a user operates a Component (presses a button, clicks on a choice, etc), the action event is called. The Event e argument contains much information about the coordinates of the event, the time it occurred, and the kind of event that it was. If the event was a keypress, it has the value of the key. The other argument is a generalized object that contains extra information in some cases.

The event contains a field called id that indicates the type of event it is (e.g., KEY_PRESS, MOUSE_DOWN, LIST_SELECT, etc), and hence which other Event variables are relevant for the event. For keyboard events, the field called key will will contain a value indicating the key that was pressed/released and the modifiers field will contain the modifiers (whether the shift key was down, etc).

9.3 AWT Widgets

Widget Wrap-Up

Sample code to create the widgets above in an applet will be provided here later.

9.4 Mouse and Keyboard Input

The previous section described how various widgets interact with the user. This section describes how mouse and keyboard events are passed to your program. It is done in a very similar way to what we have already seen for widgets.

Events go to the Component that is the immediate Container of the place where the event happened. The class Component has a method handleEvent() with this signature:

public boolean handleEvent(Event evt)
This function is essentially a switch based on the type of event that occurred and factoring out the keyboard and mouse events calling individual methods to handle all possibilities.

A list of some of the specific Event handlers that it factors out:

  1. mouseDown
  2. mouseDrag
  3. mouseUp
  4. mouseMove
  5. mouseEnter
  6. mouseExit

  7. keyDown
  8. keyUp

  9. action // when a widget is touched
The programmer can override these to provide the callback routines that window systems use for event handling.

To see what's really going on with events, insert this line at the start of your version of handleEvent:

System.err.println("Event: " + e.toString()); 
You will be amazed at the number of events that are generated by even the simplest mouse motions. This method converts each such event to a string and then prints it.

9.5 Layout in a Container

The following layout managers can be used:

Useful Methods of Components

10. Graphics Programming

This chapter looks at how to draw shapes on a Canvas or a Panel.

Table: Common Graphics Methods

This table lists the methods that cause the screen to be displayed.

Method Description
void repaint() You or the window system may call this to request that the window be refreshed. Typically, you would call it if you have changed the appearance of something, and you want to see it on the screen. It calls update()
void update(Graphics g) This is a step in repainting the window. It defaults to clearing the area then painting it, but you can conceivably override it to do something additional. However, most of your programs will not override this, and will not call this.
void paint(Graphics g) Will be called by the window system when the component needs to be redisplayed. You will not call this. You will override this if you dynamically change the appearance of the screen, and want to see it appear.

So When Do I Call These Methods?

If you just use the static display of a GUI you might never need to override any of the three above methods. You can often just hide() and show() Components as needed.

Normally the window system keeps track of what you have put on the screen. If you obscure it with other windows and then bring it to the front the window system is responsible for for restoring the state.

If you want to change what you have out on the screen this would be accomplished by overriding paint(). Code in init() can get something on the screen to begin with. Code in paint() can change the screen and get something different up there. You call repaint() to signal to the window system that it needs to update the screen. The window system will then call your paint() method to put the new image on the screen. It's done this way because paint takes an argument (a GRaphics context_ that you don't normally have (or need to have) access to. repaint() doesn't need any arguments.

repaint() calls update() which calls clear() and then paint(). You might override update() if you are doing some advanced graphics work and you know that you only need a small portion of the screen to be changed (e.g., in animation). update() gives you the opportunity to achieve this, by providing a point where you can insert your own code between repaint() and paint(). In addition

repaint(x, y, w, h)
will repaint just the stated size rectange at the given coordinates. Paint may be called by the runtime independent of update.

Summary:

You never call paint() yourself. You may override it, but the understanding is always that it will be called for you at the times the window system thinks it needs to update the screen. If you want to force the window system to think that, call repaint().

repaint() simply lodges a request to update the screen, and returns immediately. Its effect is asynchronous, and if there are several paint requests outstanding it is possible that only the last paint() will be done.

10.1 Colors, Fonts

The basic color model is a common one in the computer industry.

Font Metrics

10.2 The Graphics Context

A Graphics object is what you ultimately draw lines, shapes and text on. It is also called a "graphics context" in some window system because it bundles together information about a drawable area, plus font, color, clipping region, and other situational factors.

It's an abstract class, so it cannot be instantiated directly.

The most common way to obtain a Graphics object is as the argument to a paint() routine in a Canvas or a Panel.

Less commonly you can explicitly ask for the Graphics object belonging to any Component or any image with the call

myComponent mc = ...

Graphics mg = mc.getGraphics(); 
When you call getGRaphics is is usually for an Image, Panel, or Canvas. It's unlikely you'll get the Graphics object for anything else, such as a button.

Drawing Text, Lines, and Shapes

Methods of Graphics that draw text, lines and shapes.

Most of them come in two varieties: a drawXXX and a fillXXX.

10.3 Images

Loading and Drawing Images in

10.4 Animation

If you have the frames it's easy.

12. Future Developments (or Where Do We Go From here? )