6: More About Classes

What We Will Cover


Illuminations

Questions on Completed Assignments?

Questions from last class?

6.1: Static Fields and Methods

Objectives

At the end of the lesson the student will be able to:

  • Code static fields and methods in a class
  • Call static fields and methods from other classes
  • Describe when to use static fields and methods

6.1.1: About Static Fields and Methods

  • Static fields and methods do not belong to objects, but to the class itself
    • Sometimes called class fields and class methods
  • Create static fields like instance variables, but with the keyword static
  • Usually add an initial value as well
  • For example:
  • private static int numObjs = 0;
    public static final double PI = 3.14159;
    
  • Note that final static fields are one of the few times fields should be declared public
  • Create static methods like instance methods, but with the keyword static
  • For example:
  • public static int getNumObjs() {
        return numObjs;
    }
    
  • Note that static methods can only refer to static fields
  • Static methods usually receives all its data as arguments
  • Static methods are often used as general purpose methods, such as the Math methods

Static Imports

  • Static import is a Java feature that simplifies references to static constants and methods of a class
  • For example, you could import the static fields and methods of the Math class as shown in the following example:
1
2
3
4
5
6
7
8
9
import static java.lang.Math.*;

public class MathStuff {
    public static void main(String[] args) {
        System.out.println("E: " + E);
        System.out.println("PI: " + PI);
        System.out.println("pow(2, 3): " + pow(2, 3));
    }
}

6.1.2: Example Using static Fields and Methods

  • Classic example: keep track of how many objects of a class are created
  • First add an object count variable to the class
  • private static int numObjs = 0;
    
  • Need to increment the variable whenever a new object is instantiated
  • Can add an increment statement to the object constructors
  • public Product() {
        name = "Unknown";
        price = 0.0;
        numObjs++;
    }
    
    public Product(String newName, double newPrice) {
        setName(newName);
        setPrice(newPrice);
        numObjs++;
    }
    
    
  • Also need a method to retrieve the count
  • public static int getNumObjs() {
        return numObjs;
    }
    
  • Note that a static method does not have an object reference
  • Thus cannot refer to a (non-static) instance variable of the class
  • Likewise, a static method cannot call a non-static method of the class
  • Putting everything together we get the Product2 class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class Product2 {
    // Instance variables
    private String name;
    private double price;
    private static int numObjs = 0;

    // Constructors
    public Product() {
        name = "Unknown";
        price = 0.0;
        numObjs++;
    }

    public Product(String newName, double newPrice) {
        setName(newName);
        setPrice(newPrice);
        numObjs++;
    }

    // Instance methods
    public String getName() { return name; }
    public double getPrice() { return price; }

    public void setName(String newName) {
        if (newName == null || newName.length() == 0) {
            name = "Unknown";
        } else {
            name = newName;
        }
    }

    public void setPrice(double newPrice) {
        if (newPrice > 0.0) {
            price = newPrice;
        } else {
            price = 0.0;
        }
    }

    public static int getNumObjs() {
        return numObjs;
    }

    // For testing
    public static void main(String[] args) {
        Product2 prod = new Product2("Milk", 3.95);
        Product2 prod = new Product2("Bread", 1.98);
        System.out.println("Number products="
            + getNumObjs());
    }
}

6.1.3: Coding Static Initialization Blocks

  • When you need more than one statement to initialize a static field, use a static initialization block
  • public class ClassName {
        // field declarations
        static {
            // initialization statements
        }
        // rest of the class
    }
    
  • For example:
  • private static int numObjs;
    static {
        numObjs = 0;
        numObjs++;
    }
    
  • Runs as soon as any method of the class is called

6.1.4: When to Use Static Fields and Methods

  • When you need multiple objects of a class, use regular fields and methods
  • If you only need a general purpose utility method, use static methods
  • For example, the Math methods
  • When you need constants shared by many classes, use final static fields
  • For example:
    • Math.PI (approximately 3.14159)
    • Math.E (base of natural logarithms, approximately 2.72)

Further Information

Exercise 6.1

Take one minute to prepare an answer the following questions.

  1. What would be the results of attempting to compile and run the following program?
1
2
3
4
5
6
7
8
9
10
11
12
13
class MyClass {
    static MyClass ref;
    String[] args;

    public static void main(String[] args) {
        ref = new MyClass();
        ref.func(args);
    }

    public void func(String[] args) {
        ref.args = args;
    }
}

6.2: Working with Packages and JARs

Objectives

At the end of the lesson the student will be able to:

  • Describe the general procedure for creating a directory structure for a package
  • Add classes to a package and compile classes in that package
  • Make classes in a package available to other classes
  • Create a JAR file for an application
  • Run applications out of a JAR file

6.2.1: Packages and import Statements

  • Java allows you to group classes into packages
  • In essence, packages are Java's way of forming libraries
  • Using packages makes it easier to find and use classes
  • After organizing classes into packages, you can use them by specifying the fully-qualified class name
  • java.util.Scanner input =
        new java.util.Scanner(System.in);
    
  • This quickly becomes cumbersome, so the usual practice is to import classes
  • import packagename.ClassName;
        or
    import packagename.*;
    
  • Using the '*' wildcard will import all classes in a package
  • Some examples:
  • import java.text.NumberFormat;
    import java.util.Scanner;
    import java.util.*;
    import javax.swing.*;
    

6.2.2: Package Names and Directories

  • A package name is a form of path name to a directory
  • To store your classes in packages, you must create a directory structure for the packages
  • Then, you must store each class in the correct directory
  • For instance, if you have an application name store, then you would have a root directory for the application named store
  • In the root directory you would place the Java file with the main() method that starts and runs the application
  • In addition, any subdirectories for the application are placed in the root directory of the application, such as business and util in the following example
  • Each subdirectory then contains Java files and other subdirectories

Example Directory Structure

store
    Artzy.java
    edparrish
        business
            Product.java
            Customer.java
        database
            ProductionDB.java
        util
            MyUtils.java

Using package Statements

  • Each package name must correspond exactly to the directory names.
  • For instance, after you place the MyUtils.java class in the directory structure, you must specify a package of:
  • package edparrish.utils;
  • The package statement must be the first statement in the file
  • Here is a complete class example:
1
2
3
4
5
6
7
8
9
10
11
12
// comments can preceed package, but nothing else
package edparrish.utils;

public class MyUtils {
    public static double average(double[] values) {
        double total = 0.0;
        for (int i = 0; i < values.length; i++) {
            total += values[i];
        }
        return total / values.length;
    }
}

Using import Statements

  • To use the MyUtils.java file you must specify an import statement like:
  • import edparrish.utils.MyUtils
  • For a complete example:
1
2
3
4
5
6
7
8
9
import edparrish.utils.MyUtils;

class MyUtilsTest {
    public static void main(String[] args) {
        double[] testVals = {1.0, 2.0, 3.0};
        double x = MyUtils.average(testVals);
        System.out.println(x);
    }
}

More About Package Names

  • Note that Sun recommends using reversed Internet domain names to uniquely identify packages
  • com.edparrish.business
  • Since each Internet name is unique, this ensures a unique package name
  • Even if you do not follow this convention, you should be careful about using generic package names
  • For instance, business is too generic, but edparrish.business is specific enough that it is unlikely to conflict with other packages
  • Also note that package names are always lower case by convention

Further Information

6.2.3: Compiling Classes in a Package

  • Some Java tools cannot compile classes that have been organized into packages
    • For instance: TextPad
  • To compile classes in these cases, you can either:
    • Compile from the command line
    • Set a CLASSPATH for the system
  • Note that before you compile a class that imports another class, you must compile the class it imports.

Compiling Classes from the Command Line

  • Make sure that the classes have all of the appropriate package and import statements.
  • Start the command prompt and navigate to the root directory for the package (the parent directory).
  • Use the javac command to compile the classes.
  • Be sure to include the subdirectories in the path that correspond to the package name.
  • Syntax:
  • C:\parentDir>javac packagePath/ClassName.java
  • For example:
  • C:\store>javac edparrish/utils/MyUtils.java
    

Setting a Class Path

  • To help the Java compiler find classes that are not part of the JDK, you set the CLASSPATH
  • The CLASSPATH is an operating system environment variable that Java searches for class names
  • Thus, the way you set your CLASSPATH variable depends on your operating system
  • Note that the CLASSPATH variable is usually spelled as one work in all uppercase letters
  • You can set the CLASSPATH either temporarily or "permanently"
  • To set a CLASSPATH only while compiling, you can use:
javac -classpath /path/to/app/root packagePath/ClassName.java
  • If you have problems adding a CLASSPATH, try putting double quotes around the path names

More Information

6.2.4: Working with JAR Files

  • When you compile a class that contains a package statement, it becomes part of the package and is not accessible to classes outside the package
  • To make a package available, you can either:
    1. Add the directory structure and classes to the application
    2. Create a JAR file and add the JAR to the application's root directory
  • JAR: Java ARrchive files
  • Bundles classes and packages with a ZIP file format that retains the directory structure

Creating JARs

  • JARs are created with the Java Archive Tool
    • Provided as part of the JDK
  • Invoked from the command line
  • Syntax for creating files:
  • jar cfv jarFile inputFile(s)
    
  • For example, to place HelloWorld in a JAR file named Hello.jar:
>jar cfv Hello.jar HelloWorld.java HelloWorld.class
added manifest
adding: HelloWorld.java(in = 452) (out= 266)(deflated 41%)
adding: HelloWorld.class(in = 427) (out= 291)(deflated 31%)

>dir *.jar
...
HELLO    JAR         1,148  09-21-02 11:28a Hello.jar
  • You can then view the contents of the JAR file using:
  • >jar tf Hello.jar
    META-INF/
    META-INF/MANIFEST.MF
    HelloWorld.java
    HelloWorld.class
    

Using JARs

  • You can either run the program in a JAR directly or extract the contents
  • Syntax for running an application packaged as a JAR file:
  • java -cp jarFile mainClass
    
  • For example:
  • >java -cp Hello.jar HelloWorld
    Hello, world!
    
  • Syntax for extracting the contents of a JAR file:
  • jar xf jarFile [archivedFile(s)]
    
  • For example:
  • >jar xfv Hello.jar
      created: META-INF/
    extracted: META-INF/MANIFEST.MF
    extracted: HelloWorld.java
    extracted: HelloWorld.class
    

Further Information

6.2.5: Summary

  • You can organize classes in your application by using a package statement to add them to a package
  • package edparrish.business;
  • Then, you use import statements to make the classes in that package available to other classes
  • import edparrish.business.Product
  • To compile classes in a package, you must sometimes use the command line
  • Syntax:
  • C:\parentDir>javac packagePath/ClassName.java
  • For example:
  • C:\store>javac edparrish/business/Product.java
  • Rather than moving to the package (application) root, you can specify a CLASSPATH
  • To compile you would use something like:
javac -classpath /path/to/app/root packagePath/ClassName.java
  • When you compile a class that contains a package statement, it becomes part of the package and is not accessible to classes outside the package
  • To make a package available, you can either:
    1. Add the directory structure and classes to the application
    2. Create a JAR file and add the JAR to the application's root directory
  • For example:
  • >jar cfv Hello.jar HelloWorld.java HelloWorld.class
    
  • To view the contents of the JAR:
  • >jar tf Hello.jar
  • To extract the contents of a JAR file:
  • >jar xfv Hello.jar
  • To run the program out of a JAR file:
  • >java -cp Hello.jar HelloWorld

Exercise 6.2

Take one minute to prepare an answer the following question.

  1. Given the following class, which of the following are valid ways to call methods of the class from outside the package com.edparrish.mystuff?
  2. package com.edparrish.mystuff;
    
    public class GoodStuff {
        // ...
    }
    
    1. With a reference to the class GoodStuff
    2. With a reference to the class mystuff.GoodStuff
    3. With a reference to the class com.edparrish.mystuff.GoodStuff
    4. By importing com.edparrish.mystuff.GoodStuff and referring to the class as Goodstuff
    5. By importing package com.edparrish.* and referring to the class as mystuff.GoodStuff

6.3: Javadoc

Objectives

At the end of the lesson the student will be able to:

  • Generate documentation for a package
  • View the documentation for a package
  • Discuss the use of CheckStyle to test your documentation

6.3.1: Review of Javadoc

  • Two important principles of OOP are encapsulation and data hiding
  • Programmers using classes and the methods need to know how to use a class and its methods, but do not need to see the code for a class
  • The publicly available methods of a class are known as its application programming interface (API)
  • Java provides a tool named javadoc that automatically extracts the interface of a class from its definition
  • The result of running the tool is several HTML files that describe the API

Javadoc Comments

6.3.2: Generating and Viewing the Documentation

  • Instructions for Creating Javadoc
  • Note: to run the Javadoc tool in the classroom, you will need to both save your source code and run the tool in the C:\TEMP directory.

Viewing the Documentation

  • You can use a Web browser to view the generated documentation
    • Looks the same as the on-line Java API
  • Point the browser to the index.html file that is created in the directory you specify
  • Note that the documentation does not show the code in the class
  • This allows other programmers to use your code without knowing how it is implemented

6.3.3: Using CheckStyle

  • Cumbersome to check your code for programming style
  • Instead, you can use your computer to accomplish the task
  • Use a program named CheckStyle
    • Free and open source
    • Used for major Java projects like Jakarta
  • I provide instructions for installing and using CheckStyle for both jEdit and TextPad

6.3.4: Summary

  • You can use Javadoc comments to document a class and its fields, constructors and methods
  • You then use the javadoc tool to generate HTML-based documentation for your class
  • Programmers use the documentation to understand how to use your code
  • You can use CheckStyle to automatically check many aspects of your programming style

Exercise 6.3

Take one minute to prepare an answer the following question.

  1. Which of the following is a valid Javadoc comment?
    1. // Calculates the balance due //
    2. /* Calculates the balance due */
    3. /* Calculates the balance due **/
    4. /** Calculates the balance due */

6.4: Introduction to Inheritance

Objectives

At the end of the lesson the student will be able to:

  • Describe how inheritance is used in Java
  • Use inheritance in your programs
  • Inherit from Java's classes

6.4.1: Introducing Inheritance

Inheritance: the ability to define new classes from existing ones.

  • Inheritance is one of the key ideas of object-oriented programming
  • Lets you define a class that inherits all the public and protected fields and methods from another class
  • A class that inherits is called the subclass (derived, child)
  • A class that is inherited from is called the superclass (base, parent)
  • In a subclass, you define new fields and methods not found in the superclass
  • Here is a UML diagram showing the inheritance of one class from another

About the Diagram

  • First section of class diagram contains the class name
  • Second section describes the attributes (data variables)
  • Bottom section describes the operations (methods)
  • Minus sign (-) marks attributes and operations that cannot be accessed by other classes
  • Plus sign (+) marks attributes and operations that can be accessed by other classes
  • Parameters are listed in parenthesis separated by commas
  • Name of the parameter is followed by a colon and the data type
  • Methods that return values are followed by a colon and the return type
  • The upward pointing arrow indicates inheritance

6.4.2: Inheritance Example

  • Deriving a new class from an existing one requires the extends clause
  • Syntax:
  • public class SubclassName extends SuperclassName
  • A subclass can only extend one superclass
    • Java does not support multiple inheritance
    • Interfaces (discussed later) achieve much of the same effect

Example Superclass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class Person {
    private String name;

    public Person() {
        name = "No name yet";
    }

    public Person(String initialName) {
        name = initialName;
    }

    public String getName() {
        return name;
    }

    public void setName(final String newName) {
        name = newName;
    }

    public void printAttributes() {
        System.out.println("Name: " + name);
    }

    public boolean sameName(Person otherPerson) {
        return (this.name.equalsIgnoreCase(
            otherPerson.name));
    }

    private final void special() {
        System.out.println("I am special");
    }
}

Example Subclass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Student extends Person {
    private int studentNumber;

    public Student() {
        super();
        studentNumber = 0;
    }

    public Student(String name, int number) {
        super(name);
        studentNumber = number;
    }

    public Student(String initialName) {
        this(initialName, 0);
    }

    public void printAttributes() {
        super.printAttributes();
        System.out.println("Student Number : "
            + studentNumber);
        //special();
    }
}

Example Test Application

1
2
3
4
5
6
7
8
9
10
11
12
13
public class StudentApp {
    public static void main(String[] args) {
        Student noName = new Student();
        Person ed = new Student("Ed", 10);
        Student buford = new Student("Buford");

        noName.printAttributes();
        ed.printAttributes();
        buford.printAttributes();

        System.out.println(ed instanceof Student);
    }
}

6.4.3: Class Hierarchies

  • Classes can be derived from derived classes
    • Subclass objects can be treated as superclass objects
    • Child classes can be parent classes
  • Classes higher in the hierarchy are called ancestor classes
  • Classes lower in the hierarchy are called descendent classes
  • Note that inheritance describes an is-a relationship
    • A Dialog is a Window
  • Reverse is not true
    • Window is not always a Dialog

For Example

  • Following is taken from the Java API
  • Shows that a superclass can have more than one subclass
  • For instance, Window has two subclasses

Another Way to Draw Inheritance

  • There are many ways to draw inheritance diagrams
  • Following is another way to show the inheritance hierarchy for Container
  • Note that the superclass is always drawn higher than the subclass
  • Container
       |
       +--Panel
       |     |
       |     +--Applet
       |
       +--Window
             |
             +--Frame
             |
             +--Dialog
    
    

Designing with Inheritance

  • Note in the inheritance diagram above that a Frame is a type of Window
  • When using inheritance in an application, important to keep "is a" in mind
  • Make sure a subclass has an is-a relationship with the superclass
    • A subclass must be a type of a superclass
  • Which of the following are valid is-a relationships?
    • Car ==> Vehicle
    • Car ==> Motorcycle
    • Student ==> Person
    • Student ==> College
  • Substitute the words "is a type of" for the arrow (==>)

6.4.4: Overriding Methods

  • Following is an example of adding an attribute in a subclass
  • public class Student extends Person {
        private int studentNumber;
    
  • Note that an attribute for the student number has been added
  • A Student has this attribute in addition to a name
    • name is inherited from Person
  • Following is an example of overriding a method in a subclass
  • public void printAttributes() {
        System.out.println("Name: " + getName());
        System.out.println("Student Number : "
                + studentNumber);
    }
    
  • Both superclass and subclass has a printAttributes method
  • Both methods have the same parameters (i.e. none)
    • Thus, they have the same signature
  • The method from the subclass overrides (replaces) the superclass method
  • Will not override the superclass if the parameters are different
    • Would have different signatures
  • Called overriding -- not to be confused with overloading
  • You can still call an overridden method using the keyword super
  • public void printAttributes() {
        super.printAttributes();
        System.out.println("Student Number : "
                + studentNumber);
    }
    

Overriding Verses Overloading

    Overriding Overloading
    • Same method name
    • Same method name
    • Same signature
    • One method in superclass, one in subclass
    • Different signature
    • Both methods can be in same class

Access Modifiers and Inheritance

  • private instance variables from the parent class are not available by name in derived classes
  • You use accessor methods to change them
  • private methods are not inherited either
  • You use public or protected to allow methods or variables to be inherited
  • Only "helper" methods should be declared private

6.4.5: Constructors and Inheritance

Superclass Constructor

  • Constructors are not inherited by a subclass
  • Instead, superclass constructors are called by the subclass constructor
  • Superclass constructors are called either implicitly or explicitly using super()

Examples of Subclass Constructors

  • Keyword extends in first line
  • Defines a subclass based on the superclass
  • public class Student extends Person {
        private int studentNumber;
        public Student() {
            super();
            studentNumber = 0;
        }
    ...
    
  • Default constructor initializes attribute studentNumber to 0
  • super() calls the parent's default constructor
  • Included automatically by Java if not explicitly stated
  • super() must be first action in a constructor definition
  • ...
    public Student(String name, int number) {
        super(name);
        studentNumber = number;
    }
    
  • The overloaded constructor passes the parameter newName to the constructor of the superclass
  • After which the Student() constructor initializes the instance variable studentNumber

More About Subclass Constructors

  • Constructors cannot be overridden
  • Constructors can be overloaded, but only in the same class
  • Also, constructors can call other constructors -- known as chaining
  • Use super() to invoke a constructor in the superclass
  • Use this() to invoke a constructor within the same class
  • Whichever is used, it must be the first action taken by the constructor
  • Only one of them can be first, so if you want to invoke both:
    • Use a call with this() to call a constructor with super()

Example of this() and super() Constructor Call

  • Class Student has a constructor with two parameters:
    • String for the name attribute
    • int for the studentNumber attribute
    public Student(String name, int number) {
        super(name);
        studentNumber = number;
    }
    
  • Another constructor within Student takes just a String argument
  • Initializes the studentNumber attribute to a value of 0:
  • public Student(String initialName) {
       this(initialName, 0);
    }
    
  • Calls the constructor having two arguments within the same class

6.4.6: Using Access Modifiers

Access Modifiers and Inheritance

  • You can use the keywords public, private and protected to control access
  • Controls access to class instance variables and methods
    • Only a particular definition (unlike C++)

Package Access: the Default

  • No access modifier: known as default or package accessibility
  • Only accessible to other classes in the same package
  • Less restrictive than private accessibility, but more restrictive than public access
  • class MyClass {
        double x;
        void foo() {}
    }
    

public Access: Interface Access

  • Least restrictive of all access modifiers
  • public fields and methods can be accessed from anywhere the class is accessible
  • public class MyClass {
        public double x;
        public void foo() {}
    }
    

private Access: Don't Touch That!

  • Most restrictive of all access modifiers
  • private fields and methods cannot be accessed from outside of the class
  • Good design practice to make all variables private and to provide accessor methods where needed
  • Auxiliary ("helper") methods are declared private often as well
  • Cannot declare a class private -- how would you use it?
  • public class MyClass {
        private double x;
        private void foo() {}
    }
    

protected Access: Inheritance Access

  • Accessible in the package of this class and any subclass
  • More restrictive than public accessibility but less restrictive than default
  • public class MyClass {
        protected double x;
        protected void foo() {}
    }
    

6.4.7: Final Classes, Methods and Parameters

Final Classes

  • Use final to prevent a class from being extended
  • For example:
  • public final class String {}
    
  • Java's String class is declared final for reasons of security

Final Methods

  • Specifies that a method definition cannot be overridden with a new definition in a subclass
  • For example:
  • private final void special() {
        System.out.println("I am special");
    }
    
  • Since you would never want the method to do anything else, it makes sense to make it final
  • Allows the compiler to generate more efficient code

Final Parameters

  • Use final in a parameter list to prevent a method from assigning a new value
  • For example:
  • public void setName(final String name) {
        name = newName;
    }
    
  • You almost never want to assign a parameter a new value
  • However, the final parameter does add more clutter to the code

6.4.8: Summary

  • You can use inheritance to create a subclass (derived, child)
  • Inherits fields and methods from a superclass (base, parent)
  • Inheritance allows you to develop hierarchies of classes
  • Subclasses can have access to fields and methods of the hierarchy
  • Deriving a new class from an existing one requires the extends clause
  • public class SubclassName extends SuperclassName
  • You can define new fields and methods in a subclass
  • You can call constructors of the superclass using the super() method
  • super(name);
    
  • Also, you can chain constructors together using this()
  • Accessibility is controlled using keywords: public, private and protected
  • If a method in a subclass has the same signature as a method in the superclass, the method from the subclass overrides (replaces) the superclass method
    • Will not override the superclass if the parameters are different
    • Would have a different signature
  • You can prevent classes form being extended with the final modifier
  • You can prevent methods from being overridden using the final modifier as well

Exercise 6.4

Take one minute to prepare an answer the following question:

  1. Given the following code, which one of the following constructors could be added to MySub without causing a compile time error?
  2. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
    class MySuper {
        int number;
        MySuper(int i) {
            number = i;
        }
    }
    
    public class MySub extends MySuper {
        int count;
        MySub(int cnt, int num) {
            super(num);
            count = cnt;
        }
    
        // Add new constructor here
    }
    
    1. MySub() {}
    2. MySub(int cnt) { super(); count = cnt; }
    3. MySub(int cnt) { count = cnt; super(cnt); }
    4. MySub(int cnt) { this(cnt, cnt); }
    5. MySub(int cnt) { super(cnt); this(cnt, 0); }

Wrap Up

Home | WebCT | Announcements | Schedule | Room Policies | Course Info
Help | FAQ's | HowTo's | Links

Last Updated: April 16 2005 @16:55:07