cs205: engineering software?
(none)
20 September 2010

Guide to Java

This document covers some of the most useful structures and classes provided by Java. It is not meant to be a comprehensive reference manual or to cover the entire language.

Backus-Naur Form Grammars

We will describe language syntax using Backus-Naur Form (BNF) grammar. Each rule in the grammar is a simple replacement:
Element ::= Replacement
This rule means whenever Element appears, it can be replaced with Replacement.

Elements that appear on the left side of any rule are called non-terminals. This means they do not appear in an utterance in the language. Elements that appear only on the right side of rules are called terminals. We use Italics for non-terminals and bold for terminals.

For example,

ArgumentList ::=
ArgumentList ::= NonEmptyArgumentList
NonEmptyArgumentList ::= Expression
NonEmptyArgumentList ::= Expression , NonEmptyArgumentList
describes a non-terminal ArgumentList that expands to a comma-separated list of expressions. We can replace ArgumentList with noting (the first rule), or with a NonEmptyArgumentList (the second rule). We can replace a NonEmptyArgumentList with either a single Expression (first rule) or an Expression followed by a , and a NonEmptyArgumentList. Note that the recursive definition means we can describe argument lists of any length using a few simple replacement rules. We can keep using the fourth rule to add additional Expressions to an ArgumentList.

Invocations

There are three different kinds of calls in Java: constructors, methods, and static methods.

Constructors

Expression ::= new ClassName ( argumentList )
ClassName ::= Identifier
ClassName ::= PackageName . Identifier
PackageName ::= Identifier
PackageName ::= PackageName . Identifier
Creates a new object of type ClassName. For example, to use the constructor specified as
   public class Set {
      ...
      public Set()
         EFFECTS: Initializes this to an empty set: {}
we would do:
new Set()

This would typically be used in an initialization statement:

Statement ::= VariableDeclaration ;
VariableDeclaration ::= OptModifier Type Identifier OptInitializer
Type ::= ClassName
Identifier ::= a valid Java identifier
OptInitializer ::=
OptInitializer ::= = Expression
For example,
    Set s = new Set();
initializes s to the object created by the Set constructor.

Methods

A method is invoked on an object:
Expression ::= MethodCall
MethodCall ::= Reference . MethodName( ArgumentList )
Reference ::= Expression MethodName ::= Identifier
The Reference is an expression that evaluates to an object. This object is the this object of the method invocation.

For example, to invoke the method specified in the Set class below:

   public void insert (String el)
         // MODIFIES: this
         // EFFECTS: Adds el to the elements of this.
we need a Set object for the Reference and a String object for the argument:
     Set s = new Set();
     s.insert("alice");
Consider the method call,
     System.out.println("Hello");
The Reference is System.out, the MethodName is println and the ArgumentList is "Hello".

Sometimes the reference object for a method call is not shown explicitly:

MethodCall ::= MethodName( ArgumentList )
There is no explicit Reference, but there is still a reference object. When there is no explicit reference, there is an implicit this reference object. So, the MethodCall syntax above is an abbreviation for:
MethodCall ::= this . MethodName( ArgumentList )
Here's an example from the union method implementation in the Set class:
    public void union (Set t) 
       // MODIFIES: this
       // EFFECTS: this_post = this_pre U t
    { 
       for (String el : t.elements()) {
           insert(el);  // short for: this.insert(el);
       }
    }

Static Methods

Some methods are not invoked on objects — that is, they have no this object. These methods are specified using the static modifier. To invoke a static method, we can use the ClassName instead of the object Reference:
MethodCall ::= ClassName . MethodName( ArgumentList )
For example, System.exit(0) invokes the static method exit in the System class.

Exceptions

There are two kinds of exceptions in Java, checked exceptions and unchecked exceptions. For now, assume RuntimeException is the only unchecking exception, and all other exceptions are checked (once we cover subtyping this will change a bit).

Handling Exceptions

The Java compiler will not allow a program that does not catch all checked exceptions. This means any operation that might throw a checked exception must be contained withing a try block.
Statment ::= TryStatement
TryStatement ::= try { StatementList } CatchBlocks FinallyBlock
CatchBlocks ::=
CatchBlocks ::= catch (Type Identifier) { StatementList }
CatchBlocks
FinallyBlock ::= finally { StatementList }
StatementList ::=
StatementList ::= Statement StatementList
The execution executes the statements in the try block. If no exception is raised, execution continues to the statements in the finally block if there is one, or to the next statement otherwise. If an exception is raised, execution jumps to the catch clauses. The catch clauses are examined in order, until one is found that matches the type of the execption. Then, the statements in the matching catch block are executed. After the catch, the execution continues to the statements in the finally block if there is one, or to the next statement otherwise.

Here's an example:

   Scanner s = null;

   try {
      s = new Scanner(new File(args[0]));
   } catch (FileNotFoundException fnfe) {
      userError("Unable to open file: " + fnfe);
   }
The statement in the try block calls a Scanner constructor to create a new scanner object. The specification for the Scanner constructor,
   public Scanner(File source) throws FileNotFoundException
      // EFFECTS: If the source if not found, throws FileNotFoundException.  Otherwise,
      //    initializes this to a new Scanner corresponding to the file source.
indicates that it may throw the FileNotFoundException. The catch clause handles this exception. The variable fnfe in the catch clauses holds the value of the thrown exception. The catch clause statement calls the userError static method to report an error.

Raising Exceptions

To raise an exception, use a throw statement:
Statement ::= ThrowStatement
ThrowStatement ::= throw Expression ;
The Expression in a ThrowStatment must evaluate to an object of type Throwable. Typically, this will be a newly constructed exception object:
   public class Set {
    ...
    public String choose() throws EmptyException
       // EFFECTS: If this is empty, throws EmptyException.  Otherwise,
       //    returns an element of this.
    {
       if (size() == 0) {
           throw new EmptyException();
       }
       ...
    }
Where EmptyException is an exception class defined in EmptyException.java:
public class EmptyException extends Exception {
   static final long serialVersionUID = 0L; // Needed to avoid compiler warning.
	
   public EmptyException () {
      super ();
   }
}
(For now, it is okay to use this as a "cookbook" structure for creating your own exception types. Later we will understand more what this is doing.)

Loops

The most useful looping structures in Java are the while statement, for statement, and iterators (described in the next section):
Statement ::= WhileStatement
WhileStatement ::= while ( Expression ) Statement
Statement ::= ForStatement
ForStatement ::= for ( InitializationExpression ; TestExpression ; UpdateExpression ) Statement
A ForStatement can be rewritten as an equivalent WhileStatement:
for ( InitializationExpression ; TestExpression ; UpdateExpression ) Statement
==> InitializationExpression ; while (TestExpression) { Statement UpdateExpression; }
The InitializationExpression is executed first. Then, if the TestExpression evaluates to true, the Statement is executed, followed by the UpdateExpression. After the update, execution continues with the TestExpression again. This continues until the TestExpression evaluates to false.

Withing the loop body statement, we can use two statements to alter execution:

Statement ::= break ;
Statement ::= continue ;
The break statement exits from the containing loop block. After a break statement, execution continues at the statement following the looping statement. The continue statment jumps to the update expression (in a for loop), or the test expression (in a while loop). It continues to the next iteration of the loop.

Here's an example from the TaskScheduler code:

   while (linescanner.hasNext()) {
      String dname = linescanner.next();
      if (dname.equals("}")) {
	 gotbrace = true;
	 break;
      }
      depends.add(dname);
   }

Iteration

Iterators provide a convenient and abstract way to perform an action on all elements of a collection. Java's mechanisms for iteration have improved significantly with Java 1.5, so what we describe here is not described in Chapter 6 of the textbook (the old mechanisms still work, but are more cumbersome).

An enhanced for statment supports iteration through a collection:

Statement ::= IterationStatement
IterationStatement ::= for ( Type Identifier : Expression) Statement
The Expression must evaluate to an object that supports iteration. This includes any arry instance, as well as any object of a class that implements the Iterable interface (we'll cover what this means a bit later, but for now assume all the collection classes support it). The Identifier is a variable that will be initialized to each value in the collection in turn in each iteration of the loop.

For example,

   static int sum(int [] values) {
       int total = 0;
       for (int val : values) 
          total += val;
       return total;
  }
We can also use this to iterate through the elements of a Vector, as in the union implementation above.

Java Collection Classes

The Java API provides a number of classes that you will find useful in Java programs. The full specification is available here: http://java.sun.com/j2se/1.5.0/docs/api/overview-summary.html.

The java.util package defines many useful classes for managing collections of objects. These classes take type parameters that specify the type of objects in the collection. The collection classes provided by Java are not good data abstractions — their specifications (and even their names) include details about how they are implemented. For example, there are three different Set datatypes: HashSet, TreeSet, and LinkedHashSet. The implement essentially the same abstraction, but with different performance characteristics; the best one to use depends on what values will be in the set and the commonly used operations. Useful collection classes include:

Follow the links above to specifications of these datatypes.

To use any of these classes, you need to import them:

   import java.util.Vector;
   import java.util.HashMap;
then, declare a variable with the type parameters specified:
   private HashMap<String, Vector<String>> table;
declares table as a HashMap where the keys are of type String and the values are of type Vector<String>.

So, when we call the put method specified as:

public V put (K key, V value)
  // EFFECTS: Associates the specified value with the specified key in this map. 
  //    If the map previously contained a mapping for this key, the old value is replaced. 
  //    Returns the previous value associated with the key, or null if there was no
  //    previous mapping.
we would pass in a String and a Vector<String> object as the arguments.