Module I: Java Syntax
In this first module of the course, we presented the Java
Language, in increasing levels of difficulty. Topics include:
- Data types (primitive and Class type)
- Control structures -- for, while, if,
else
- Operators -- (a) +, -, /, *; (b) % is the remainder
operator. Thus "x%10" returns the remainder when you divide the int variable
x by 10; (c) && and || operators for boolean and and or; (d)
++ and -- operators to either postIncrement/postDecrement or preIncrement/preDecrement;
(e)
- Design Recipes -- to explain how to structure
programs. Focus on INPUT, PROCESSING, OUTPUT. We quickly evolved these
concepts into modeling, as enabled by Module II
- Methods -- return type, name, parameters, throws
declaration, method implementation, return statements. When a method
can throw an Exception, when it must declare a throws declaration. What the
arguments to a method are
- static variables and methods -- static
is a keyboard to be assigned to a piece of data or a method to state that
the element is not associated with objects instantiated from the class, but
rather that they are associated with the class. They are referenced as
SomeClass.x;
- JavaDoc documentation standard. Focus on (a) @param;
(b) @return; (c) @exception; (d) definition of the method documentation; (e)
class-level documentation.
Much of the knowledge contained in this module is assumed
knowledge for the remaining two modules of this course, and definitely fair game
on exam questions.
Module II: Object-oriented Design Fundamentals
In this middle module of the course, we presented
object-oriented material that went much further than the Java syntax presented
in Module 1. We specifically considered numerous concepts that go directly to
the heart of Object-Oriented Design
- Encapsulation -- Gathering together into one
class the data and functionality that capture a specific abstract concept.
- Access Modifiers -- One can ascribe visibility
modifiers to the various elements of a class, such as data, methods, and
constructors. Even the class itself can be declared with its own visibility
level. The possible types are: (a) public -- wide open visibility;
any class can access the element; (b) private -- no class other than
the one in which the element is defined can access the element. Note that
this has caused some confusion in the past, specifically on exam 2. Look at
the solution for
Exam2, specifically the RationalNumber equals method; (c) default --
if you leave out the visibility modifier, then the element is visible only
to other classes within the exact same package as the class; (d)
protected -- expose the information not only to those classes within the
package in which it is defined, but also enable other classes which
derive from the class to see the information, regardless of which
package in which they might exist.
- Inheritance -- The ability to create a new
class by extending an existing class. The base class provides the
non-private data and methods that are inherited for free by the
derived class, and able to be used. The derived sub-class can add methods
and/or data for use by the sub-class. The derived sub-class can override
specific methods to alter their functionality; within these altered methods,
the super keyword can be used to access the original method
definition as provided by the base super-class.
- Constructors -- defined to take parameters
useful for initializing the class. The use of super(paramList) to
also use constructors defined by the superclass. Note that you do not create
multiple objects by this invocation. It is like an overlay. When a derived
class has a constructor that invokes super() with some parameters,
the constructor of the derived class is saying "have my base class
initialize itself in its own fashion using the arguments I provide, and then
I will take care of my own business". Any invocation to super() must
be the first line of a constructor.
- This -- keyword to be used within an instance
method that refers to the object on which the instance method was invoked;
can also be used in the constructor to refer to the newly created object.
This is typed according to the class in which the method is defined. You
can use this as an argument when invoking other methods.
- Interfaces -- Defines a set of methods that
must be defined; it provides no implementation. Once a class states
categorically that it implements an interface, that class must
implement the methods contained within the interface or the class can
simply mark itself as abstract, delegating such requirement onto the
future derived classes.
- Abstract Classes -- When a class provides only
the skeletal framework for a set of future derived classes, it can be marked
as abstract which means that objects from this class cannot be
instantiated. However, in all other respects, it acts as a type. Thus you
can have parameters and variables be defined to be of this type. What this
means at run-time is that an object from one of its derived classes is going
to be the allowed value. Unlike interfaces, an abstract class can provide
some implementations that can be shared among a set of derived classes.
- A Pure Abstract Base Class -- has no provided
method implementations. It differs from an interface in at least two
respects: (a) A pure abstract base class can define a constructor that is to
be shared by the derived classes; (b) A pure abstract base class can define
non-private data that can be stored for use by derived subclasses
- Overloading -- As a convenience mechanism to
users of a class, a set of related methods can all have the same name with
different parameters.
- Exceptions -- Checked Exceptions and
Runtime-exceptions
- Polymorphism -- The ability for a method to be
defined in a base-class, and different implementations can appear within the
derived sub-classes.
- Late Binding -- The ability at run-time to
execute the appropriate method based upon the class of the object. Note that
this concept is intimately related to polymorphism and inheritance. The
classical example is going to be the equals(Object o) method which is
defined in java.lang.Object
- Boxing/UnBoxing -- The ability (finally
implemented in JDK 1.5) to effortlessly converted between primitive types (int,
char, boolean, float, double, long) with the counterpart Wrapper classes (java.lang.Integer,
java.lang.Character, java.lang.Boolean, java.lang.Float, java.lang.Double,
java.lang.Long).
- Null -- the keyword representing the concept
"points to no object". So when you say "String s = null;" you are saying
"variable s is of type. It can point to any object of class String or one of
its derived classses. However, in this case, the variable s does not point
to an object." Don't say "the empty object" since that is just confusing.
Note that the empty string "" is not the same as null. The empty
String "" is a String object whose length() is zero.
Some core Java classes were presented. These are classes
that I expect you are aware of for the exam:
- java.lang.Object -- The base class of all Java
classes. The methods that you must know are: (a) boolean equals (Object
o); (b) int hashCode(); (c) String toString(); (d)
Class getClass() method which can be used to see if two objects belong
to the exact same class.
- java.lang.String -- We have used this repeatedly.
Methods for use are: (a) int length(); (b) char charAt(int);
(c) boolean equals (Object o); (d) int indexOf(char); (e)
String substring (int start, int end); (f)
- Wrapper classes java.lang.Integer,
java.lang.Character, java.lang.Boolean, java.lang.Float, java.lang.Double,
java.lang.Long
- java.util.Scanner -- Provides the ability to access
information from a File or from the keyboard; (a) Scanner constructor with
System.in; (b) Scanner constructor with a java.io.File object; (c)
hasNext(); (d) nextInt(); (e) nextLine(); (f) next();
(g) nextDouble(); (h) nextLong(); (i) nextFloat();
- java.lang.IndexOutOfBoundsException (unchecked)
- java.lang.NullPointerException (unchecked)
- java.lang.StringIndexOutOfBoundsException (unchecked)
- java.lang.IllegalArgumentException (unchecked)
- java.io.FileNotFoundException (checked exception)
- java.util.NoSuchElementException
- java.util.Hashtable [see method described in the next
Module III]
- java.util.ArrayList [see method described in the next
Module III]
- java.util.Iterator interface: (a) boolean hasNext();
(b) Object next(); (c) void remove()
- java.io.File: (a) new File (String fileName);
We spent a lot of time working over the linked list
concept. This was done, primarily, because the linked list is the fundamental
dynamic data structure used in Computer Science. We developed functionality such
as:
- Create a List class to manage the head of the list
(and be the place where functions are defined) and create a Node class to
manage the links in the chain
- Prepend a node to the List
- Append a node to the List
- Remove a Node from the List
- Insert a Node into the List at the appropriate
location
- Count the nodes in a List
- Create a String representation of a List
- Create different List/Node classes based upon
different value types.
See closing notes on November 20th.
We also dedicated a good amount of time in this class on
Unit testing, specifically with regards to the JUnit test framework that is
integrated into Eclipse. As such you should be able to:
- write a 'public void testSomething() { ... }' method
which can be used as a test case
- use assertEquals (x,y)
- use assertTrue (cond) and assertFalse
- use fail("message")
Module III: Object-oriented Design Guidelines
In this last module of the course, we presented less
material in class, and focused more on addressing high-level concepts. These
concepts have appeared in the class handouts, and as practical applications on
the homeworks and the daily code examples:
- Design for simplicity --
When reviewing a design, select simpler alternatives when
available.
- Don’t Use Inheritance just because you can --
Understand the difference between HAS-A relationships and IS-A
relationships.
To say that class BusinessItem is-a Item, you must be able to
state categorically that (a) Everything you can say about Item you
can also say about BusinessItem; and (b) there may be special data
and functionality that are associated with BusinessItem that are not
associated with Item.
From an external perspective, any method that you could invoke on an Item
you could also invoke on a BusinessItem.
To say that class Stack has-a ArrayList, you are
declaring that there is an association between these two classes.
Specifically, the Stack class may have some responsibilities (made
manifest as public functions) that can only be carried out because it
maintains an internal reference to an object of the ArrayList class.
To uncover has-a relationships, look at the responsibilities assigned
to a class, and review its data attributes to see if it can even meet its
responsibilities.
Note that it would be a mistake to design Stack to extend
ArrayList. That is, to declare that a Stack class is-a
ArrayList. Why? Consider the methods in ArrayList that are just
not meant to be used on a Stack class, but these are inherited for free from
the ArrayList class and cannot be blocked for use by the users of
Stack. Anytime you say "X is just like Y except that a set of methods
meant for Y cannot be used on X" then you cannot use X is-a Y.
- Know which classes know which other classes.
Focus on the Responsibilities and limit
tanglement as much as you can. If you find that a
class seems to know much about a lot of other classes, it perhaps should be
sub-divided into a set of classes, each of which takes on some partition of
the responsibilities that had been assigned to the original class.
- Realize there is a space vs. time tradeoff.
Don’t store two values when one can be computed from the other [unless
computation is exorbitant]. Consider the costs, both in terms of programmer
effort and ultimate program execution time.
For example, with just a little extra cost, you can make the append and
prepend methods of NumberList quite efficient.
 |
See code examples under dec12 package. |
- Know when to split a class from doing too
much. Consider the responsibilities of a class, and
see if it is doing two (or more) things when it could better delegate some of that
responsibility onto another class. Two classic examples of this which we
have seen include (a) AmidaBoard where the LineNode class takes on
some responsibility that releases the AmidaBoard from having to know
too much about the way edges are maintained; and (b) the Tic Tac Toe Game
class, which delegates the storing of the Grid to a Grid class so it
can focus on the placing, setting, and clearing of marks.
- Know when to merge two classes (or information
concepts) that somehow were separated at birth. Consider the HW3
solution that asked you to define a Molecule as having an array of int[]
representing the count of the elements found in an array of Element[]
elements. What was lacking from this solution was the realization that there
was a basic part Compound which actually pulled together the
repetition count and the Element into a single unit.
- Observe parameters to methods to locate
methods that have been defined in
the wrong class.
You could define the following horrible method which has
no right to be defined within NumberNode.
See code examples under dec12
package. |
/** HORRIBLE IMPLEMENTATION. Found within
NumberNode class
public void deleteFrom (NumberList node) {
if (node.head == this)
{
node.head =
next;
} else {
NumberNode
n = node.head;
while
(n.next != this) {
n = n.next;
// run off the end? Gone!
if (n == null) {
return;
}
}
// if we
get here, then the next of 'n' points to this, so we can remove
n.next = next;
}
} |
- Design for Change. Keep implementation
details hidden behind well-defined interface.
One of the reasons we mandate all instance variables to
be non-public is that you don't want external classes to somehow develop a
dependence on the particular way that a class is implemented. Once this has
occurred, then the dependent code would break if the original class designer
chose to replace, say, a fixed array int[]ar; with a more flexible
ArrayList has-a implementation.
- Design for Change. Expose meaningful
methods that serve a distinct purpose. Instead
of exposing key details about how a particular class is structured, you
could expose an Iterator that would enable someone to retrieve the
elements of that structure without revealing that the structure is: an
array, an ArrayList, a LinkedList, or a Hashtable.
- Know your Data Types. In this class, you have
seen: (a) fixed arrays of a primitive type; (b) fixed arrays of a class
type; (c) LinkedList structures, where you design a List and a Node class to
maintain values of some type in a linear linked list; (d) ArrayList object
to manage dynamically growing arrays of a class type; (e) Hashtables, which
can be used to associate <key, value> pairs. Each has its usages, and you
should be able to look at a problem and instinctively be able to apply the
proper data structure at the proper place.
- Know the Hashtable/Map. Know that the
following methods are present in the Hashtable and can be invoked on
an object ht of Hashtable: (a) Object put(Object key, Object
value); (b) Object get (Object key); (c) boolean containsKey (Object key);
(d) boolean containsValue(Object value); (e) int size(); (f) create an
Iterator over the keys by using keySet.iterator(); (g) create an
Iterator over the values by using values().iterator().
- Know the ArrayList. Know that the following
methods are present in the ArrayList and can be invoked on an object
ar of class ArrayList: (a) int size(); (b) Object get(int
index); (c) int indexOf(Object o); (d) Object remove (int index); (e)
boolean remove (Object o).
The culmination of the experience from this third module
will enable you to solve initial design problems as outlined here: