Java Programming Language Training Exercises
Compiled and maintained by
Ravi Jasuja,
Bill Schneider,
Tracy Adams, and
Shan Shan Huang.
- Objectives:
- Give programmers new to Java a quick introduction to the Java development process, the Java
language and object oriented (OO) design.
- Give a quick review for programmers with past Java experience.
- Provide a quick introduction to JSP.
- Certify ArsDigita programmers so that they can work on Java projects.
- Audience:
- Mainly programmers with little or no Java and JSP experience.
- Object Orientated design experience is not necessary.
- Time required:
- New to Java and OO - 40+ hours (depends on programming skills)
- New to Java - 30-40 hours
- Experience with Java - ~10 hours
- Training setup requirements
- Maintainers
- Please send email to all of us. (We are setting up an alias)
- Last Updated
- March 13, 2001, by Shan Shan Huang
- Reading material and related documents
Now get to work.
Exercise 0: Background reading
Read an overview of the Java and object oriented programming in one of the following:
- The Java Tutoria1
- Thinking in Java - Chapter 1: Introduction to Objects
- Read the documentation for JUnit setup. NOTE: the documentation is in your Junit download directory,
doc/cookbook/cookbook.htm
. You should write a JUnit test class for every exercise below.
Exercise 1: Getting comfortable with the environment
Learn how to write your first program. Read one of the following:
Compile and run the "Hello World" or "HelloDate" program on your system. (These instructions assume you are working with Unix - see Your First Cup of Java for other platforms.
- Create a directory named
java
in your home directory.
- Set up your
CLASSPATH
variable. The CLASSPATH
variable is one way to tell Java applications where to look for needed class files.
- Check your environment variables to see if
CLASSPATH
contains ~/java
<$ bash
$ env
PWD=/home/teadams/java
ORACLE_SID=ora8i
TZ=US/Eastern
HOSTNAME=dev0103-001.arsdigita.com
LD_LIBRARY_PATH=/ora8/m01/app/oracle/product/8.1.6/lib:/usr/lib:/usr/
local/lib:/ora8/m01/app/oracle/product/8.1.6/ctx/lib:/ora8/m01/app/or
acle/product/8.1.6/jdbc/lib
CLASSPATH=/ora8/m01/app/oracle/product/8.1.6/jdbc/lib/classes111.zip:
/ora8/m01/app/oracle/product/8.1.6/jdbc/lib/classes12.zip:/ora8/m01/a
pp/oracle/product/8.1.6/jdbc/lib/nls_charset12.zip
NLS_DATE_FORMAT=YYYY-MM-DD
MANPATH=/usr/share/man:/usr/openwin/share/man:/usr/local/man
USER=teadams
- update the
CLASSPATH
to add ~/java
if it is not there already
$ export CLASSPATH=$CLASSPATH:~/java
- This will only update your
CLASSPATH
for the current session. If you would like this change to be permanent, put the last line in your ~/.profile file.
- Cut and paste HelloWorldApp or HelloDate into your text editor. Save the program as HelloWorldApp.java or HelloDate.java.
- Compile the program:
$ javac HelloWorldApp.java
- Break a line of code and try compiling again to see what happens. Fix your code and recompile.
- Run the program
$ java HelloWorldApp
- Read about JavaDoc in one of the following:
- Add Javadoc comments to your program.
- Make your class public.
- Create a directory name documentation in your java directory.
- Run javadoc to generate your documentation.
- javadoc -d documentation <classname.java>
The documentation directory will contain documentation similar in format to JavaTM 2 Platform, Standard Edition, v 1.3 API Specification. You can view the html files in the documentation directory. If you are curious, move your files to a web server and view the documentation for your class.
Note: ACS makes use of javadoc in several places (tcl, jsp, adp, ajb scripts and procedures) to generate documentation for the API browser.
Exercise 2: Recursion and Iteration
Dig deeper into the language by reading either:
Write a class named Fib whose
main
method computes
the n
th number in the Fibonacci sequence
(1, 1, 2, 3, 5, 8, 13, etc.) both
iteratively and recursively. The fibonacci sequence is defined by the
recurrence:
fib(n) = fib(n-1) + fib(n-2)
and the base cases:
fib(0) = 0
fib(1) = 1
This class, as well the rest of the classes in this problem set, should be put in a package called pset.
To create a package pset:
- Create a directory in ~/java called pset.
- Put your java files in the pset directory.
- The following should be the first non-comment line in each of your java files.
package pset;
In addition, they all should be commented using javadoc.
Your main
should take two command line arguments.
The first is a string with values "I" or "R" to select the Interative
or Recursive versions, respectively. The second is an integer, which is
the argument to fib() (Remember to convert this from String to
int. Hint: Use Integer.parseInt)
.
You should be able to run your fibonacci program as follows (note the package name):
$ java pset.Fib R 10
Use the UNIX time command to time both versions on fib(10) and
fib(40). Is there a lesson to be learned here?
Exercise 3: A Polynomial Class
Add a class Polynomial to your pset package representing
polynomials (mathematical expressions
of the form a0 + a1*x + a2*x^2 + a3*x^3 + ... + an*x^n) with
integer coefficients. Implement the following methods:
/** Constructor from an array of coefficients c[] where c[i] is the
coefficient of the x^i term */
public Polynomial(int[] coef) {
}
/** returns the power of the highest-order non-zero term. */
public int degree() {
}
/** returns a string representation of the polynomial (use
"x" as the dummy variable and format high-order to low-order powers */
/** Your string should be in the following form:
* x+2*x^2-5*x^5+x^6
*
* NOTE: if your string does not follow the above format, the provided
* parser functions will not work for you!
*/
public String toString() {
}
/** operations on Polynomials returns this + a */
public Polynomial add(Polynomial a) {
}
/** returns this - a */
public Polynomial sub(Polynomial a) {
}
/** returns this * a*/
public Polynomial mul(Polynomial a) {
}
/** returns this * c */
public Polynomial scale(int c) {
}
Note that you don't have to implement subtraction directly, you can
implement it as
p1.add(p2.scale(-1))
.
Comment your class with Javadoc compatable comments.
Use the following process to write your classes.
- Create ~/java/pset/Polynomial.java
- Cut and paste the start from this problem set.
- Fill in class with dummy variables, casts, and return statements. Fill in your javadoc documenation. Make sure this framework compiles. This will help you make sure you understand what Polynomial is supposed to do, what object types you will need to deal with, and how these relate to other classes.
- Write a drive class to test the implementation. Try to think of test cases that will implement every type of situation. When you write your driver, make sure each test returns a definitive indication of whether or not it worked. For example, the test case should return "Failure: 2+2=4 - returned value was 5" instead of "2+2 = 5" or "5". This will make your harness more convenient to use and more useful for regression tests. (In ACS, we will be using a regression test harness, JUnit, for this purpose. We will consistently be emphasizing javadoc commenting and writing unit tests as you go.)
- Fill in your methods one by one, testing as you go.
Questions:
- This is an immutable implementation of polynomials. Unary
operators like
scale
are called "producers" since they return new
polynomials. What are the advantages and disadvantages of this
choice?
- the Polynomial class is a data abstraction because classes
that use it, like your test driver, don't need to know about its
internal representation to use it's methods. Give examples of
ways that you might change the Polynomial class internally without changing
the public interface that is shown to other classes.
Submit the source to Polynomial.java, your test driver, and your answers
to the questions.
Exercise 4: Polynomials as Functions
Since polynomials are functions of a variable x, add the following methods to
return the value of a Polynomial evaluated at x. Update your test driver accordingly.
/** Given an integer x, return the value of a Polynomial evaluated at x. */
public int evaluate(int x);
/** Given a string representation of a Polynomial, and an integer x,
* evaluate the polynomial at x
*/
public static int evaluate(String s, int x);
NOTE: for your convenience, we are providing you with a string parsing method
that returns you an integer array, given a string representation of a Polynomial.
Paste the following code into your class definition of Polynomial.
To use this method (stringToCoefs(String s)
), you must have your
string representation of a Polynomial in the following format :
2+x^2-3x^3
Otherwise the method as written will fail.
/**
* Given a string representation of a Polynomial, return an
* integer array of coefficients.
*
* @param s String representation of Polynomial.
* @return int[] integer array of coefficients.
*/
public static int[] stringToCoefs(String s) {
int thisSignIndex = 0;
int[] coefAndDegree = new int[2];
int lastDegree = 0;
s = s.trim();
if (s.indexOf("+", 1) == -1 && s.indexOf("-", 1) == -1) {
// this polynomial only has one term.
int[] coefs = new int[1];
coefAndDegree = getCoefAndDegree(s);
coefs[0] = coefAndDegree[0];
return coefs;
}
thisSignIndex = Math.max(s.lastIndexOf("+"), s.lastIndexOf("-"));
// figure out what the highest degree is and allocate
// space for int[] coefs accordingly.
coefAndDegree = getCoefAndDegree(s.substring(thisSignIndex));
int[] coefs = new int[coefAndDegree[1]+1];
coefs[coefAndDegree[1]] = coefAndDegree[0];
// loop through each polynomial term except for the last --
// since we already took care of that in the above code.
while (s.indexOf("+", 1) != -1 || s.indexOf("-", 1) != -1) {
for (int i = 1 ; i < s.length(); i ++ ) {
char ch = s.charAt(i);
if (ch == '+' || ch == '-') {
thisSignIndex = i;
break;
}
}
coefAndDegree = getCoefAndDegree(s.substring(0, thisSignIndex));
coefs[coefAndDegree[1]] = coefAndDegree[0];
// make sure to initalize the missing degrees with coefficent 0.
if (coefAndDegree[1] -1 > lastDegree) {
for (int i=lastDegree+1; i<coefAndDegree[1]; i++) {
coefs[i] = 0;
}
}
lastDegree = coefAndDegree[1];
s=s.substring(thisSignIndex).trim();
}
return coefs;
}
/**
* given one term of a polynomial, returns a int[2],
* with int[0] being the coefficient of that term,
* and int[1] being the degree.
*
* @param s String representing one term of the polynomial. i.e. -2x^2
* @return int[2] int[0] is the coefficient of the polynomial term,
* int[1] is the degree of the term.
*/
private static int[] getCoefAndDegree(String s) {
s = s.trim();
// integer array to store return result in.
int[] cAndd = new int[2];
int degree =0;
int coef =0;
// figure out wheather the coefficient should be positive of negative.
boolean minus_p = false;
int signIndex = s.lastIndexOf("+");
if (signIndex == -1) {
signIndex = s.lastIndexOf("-");
if (signIndex != -1) {
minus_p = true;
}
}
int xIndex = s.lastIndexOf("x");
int degreeIndex = s.lastIndexOf("^");
if (xIndex == -1) {
// this term is of degree 0
coef = Integer.parseInt(s.substring(signIndex+1).trim());
if (minus_p) {
coef = 0-coef;
}
cAndd[0] = coef;
cAndd[1] = 0;
} else {
// this term has an x, and thus degree > 0
if ( xIndex == 0 ) {
// there is no coefficient for this term.
coef = 1;
} else {
if ( s.substring(signIndex+1, xIndex).trim().equals("")) {
coef = 1;
} else {
coef = Integer.parseInt(s.substring(signIndex+1, xIndex).trim());
}
if (minus_p) {
coef = 0-coef;
}
}
if (degreeIndex == -1) {
degree = 1;
} else {
degree = Integer.parseInt(s.substring(degreeIndex+1).trim());
}
cAndd[0] = coef;
cAndd[1] = degree;
}
return cAndd;
}
Question:
- What are the advantages and disadvantages of choosing static
methods for binary operators vs. instance methods? (In this case we do
both).
Submit the source to Polynomial.java, your test driver output, and answer to the question.
Exercise 5: Class Design
Read more about relationships between objects by choosing either:
Design a class hierarchy (classes and/or interfaces)
to support a program dealing with geographic objects.
Support the following classes (at least):
- Countries
- States/Provinces
- Cities
- Boundary Segments
- Rivers
Support the following operations, where applicable, plus any others
that seem useful (arguments have been omitted):
area()
capital()
getCities()
getCountry()
distance() (between cities)
boundaryLength() (total length of boundary)
neighbors() (objects sharing boundaries)
borderOf() (the countries/states this separates)
Draw a picture of your classes and how they relate.
Write out the class definition, instance variables and
method definitions. Don't bother to implement the
methods (but make sure you could). Use interfaces and
superclasses where appropriate. Supply javadoc
comments for all classes, interfaces, and methods.
Note: This problem is deliberately openended. Don't panic!
Submit: Your class and method definitions (in a single text file).
Exercise 6: Priority Queue
Read more about containers in one of the following:
Priority queues are containers that hold objects that can be
compared. That is have an order relation equivalent to > (greater-than).
Objects are inserted into a priority queue in any
order, but are removed in sorted order. That is, the largest (or
smallest) element in the queue is removed and returned first. The
basic PriorityQueue interface is
public interface PriorityQueue {
// Add an Object to the queue
public void insert(Comparable a);
// Removes and returns the maximum object from the
// queue
public Comparable removeMax();
// Returns true iff queue is empty
public boolean empty();
// Returns the number of objects in the queue
public int length();
}
These queues can only hold references to objects belonging to classes
that implement the
Comparable
interface.
(Note: This interface is defined in package
java.lang so you needn't redefine it; you
will have to be
sure to
import java.lang.*
in your PriorityQueue class
though.)
interface Comparable {
// Returns a negative integer, 0, or a positive integer
// depending on whether 'a' in
// less-than, equal to, or greater than the implicit arg
// (this) in the desired ordering.
public int compareTo(Object a);
}
You should read the full definition of the Comparable
interface at JavaTM 2 Platform, Standard Edition, v 1.3 API Specification.
Follow the process outlined in Exercise 3 to
write a class PQueue that implements PriorityQueue in the pset package.
(You can just extend your Poly test driver.) Don't worry about
efficiency. Use the collection LinkedList
as the underlying data structure. It is easiest to sort the
objects as they are inserted. Make sure the structure can grow to
arbitrary size, yet properly shrinks when items are removed.
The Java String and Integer classes implement Comparable. Test your
class by inserting a handful of Strings and removing them, verifying
they come out in the right order. Test that it still work when you
interleave inserts and removes.
Modify your Poly class to implement Comparable. The ordering for
polynomials is based upon the following rules:
- a higher degree polynomial is greater than a lower degree one
- for polynomials of equal degree, compare based upon coefficients from highest order to lowest order terms
For example:
x^5 > 10*x^4 + 20*x^3
x^4 + 6*x + 10 > x^4 + 3*x + 20
Questions:
- What happens if you insert some Strings then some Integers
(remember: this is the class wrapper for int, not the int type). What
happens if you try to removeMax()? This reveals a problem with this
type of abstraction. Is there any good solution?
- The java.util package has a class TreeMap which
implements similar functionality to PriorityQueue. It
can use the Comparable interface to sort, but it also
allows the specification of a Comparator object in the
constructor. A Comparator is a class with a binary
compare() function.
The significant difference is that the Comparator is
attached to the TreeMap rather than the elements
themselves (as is the case with the compareTo method
of Comparable). Does this solve the problem
encountered above?
Submit: PQueue.java and answers to questions.
Exercise 7: A simple JSP page
Read one of the following JSP tutorials.
Up to this point we've been implementing Java classes and executing
them from the command line by explicitly invoking the JVM with the "java"
command. Now we're going to shift gears and start running Java code
over the Web, taking input from URL/form variables and displaying output
in the web browser as HTML.
A convenient way of running Java code over the Web is Java Server
Pages (JSP). JSPs contain a mixture of HTML and Java code. The HTML
is displayed directly in the browser, and the Java code is executed to
generate dynamic HTML displaying the results of some computations,
database queries, etc. JSPs are translated into Java classes, compiled,
and executed by a "JSP engine"; all of the HTML text ends up getting
translated into println
calls, whose output goes to the
browser. This turns out to be very useful because you'll usually want
to display much more formatting information on a Web page than you
would from the command line, and explicitly wrapping each line of HTML
inside a println
call can be cumbersome and hard to
maintain.
If you do not have Tomcat set up, download and install Tomcat from the Jakarta-Apache project
(http://jakarta.apache.org). This is an open-source JSP and
servlet engine. (Servlets are Java classes that process a request and
return output; JSPs are translated into servlets and can be thought of
as a servlet-on-the-fly.) After following the directions and starting
Tomcat, you should have a working HTTP server that you can access at
http://localhost:8080 (unless you configured the port number).
The notes on configuring Tomcat in Getting Started with ACS 4.0 Java will help you set up your configuration file.
Tomcat is implemented entirely in Java, which means that starting
your Tomcat server invokes the Java Virtual Machine (sometimes called
the "Java Runtime Environment") just as you did in the previous
exercises by using the "java" command explicitly. But, rather than
displaying the results of a computation to the command line, the Java
program listens to incoming HTTP requests and processes them
accordingly, which usually means generating an HTTP response to
display HTML text in the browser.
Look through the example JSP code. (If your JSP's don't work, it is likely that you need to
- Add an environment variable JAVA_HOME that points to the top level JDK directory.
- Add JAVA_HOME/lib/tools.jar to your CLASSPATH environment variable.
Note how URLs translate into
the location of JSP files in the file system;
http://localhost:8080/examples/jsp/snp/snoop.jsp serves the file
$TOMCAT_HOME/webapps/examples/jsp/snoop.jsp. Also note that JSP pages
are just like HTML pages, except they have some extra tags to escape
into Java code and display the value of Java variables.
Make two new pages: one to display a form asking the user's name,
and the other to display a hello-world message ("hello, [name], I'm
talking to you in JSP."). The form-input page can be static HTML, and
the output page should be a JSP.
Submit: the two pages you created.
Exercise 8: Using Java classes in JSPs
Now that you know how to create a basic JSP, you're going to use JSP
as an interface to the Fibonacci class you created in Exercise 1.
- copy your Fibonacci class into
$TOMCAT_HOME/webapps/examples/WEB-INF/classes/pset
. This
ensures that any web page running in the "examples" web application
will see your class. (You can also use the CLASSPATH environment
variable to tell Tomcat about other classes it should use, but copying
a file tends to be less painful and more foolproof.)
- Make an HTML form as you did previously, to ask the user what
fibonacci number they want, and whether they want it computed recursively
or iteratively.
- Make a JSP page that takes the user input from the form,
computes a result using the Fibonacci class you implemented in Exercise 1,
and generates output to the browser.
- Question: Think about how we could have implemented the iterative
version of the fibonacci function pretty easily entirely in the JSP
without using a separate class. Is this a good idea? Do we
gain anything from putting the Fibonacci logic in a separate class?
Submit the new pages you wrote, and any modifications to your
Fibonacci class, along with your answer to the above question.
Who Wrote This and When
Some of the problems were collected from the ArsDigita University
course and originally developed by Dr David Goddeau. These were
compiled and modified by Bill Schneider and Ravi Jasuja. Tracy Adams
added links to outside material, design methodoly tips, and command
line instructions. Bruce Keilin tested the problem set and made
several documentation edits.
©1999-2001 ArsDigita Corporation
Reader's Comments
Observations on this so far (having worked through most of part 3):
- It would be useful if the Javadoc in the sample code was actually complete. That is, where are the @param and @returns tags?
- The interface presented for the Poly class makes no mention of a "getter" method for the coefficient array. Given that this problem set is for beginners, it would be wise to hint at the concept of data hiding.
- You mention "producers" as a pattern for methods that do not alter the message sink object, but rather produce new objects that reflect the method's operation. This should be discussed a bit more. Beginners to Java, or any reference-based OO language (as opposed to pointer-based, such as C++) will likely be confused by this.
- JUnit is mentioned as being required. Again, beginners are not likely to understand how it works with their source code. A simple example with the Fib class would be beneficial.
-- Stephen Silber, March 15, 2001