course homepage course calendar
Tuesday, June 19
This session covers the reading in Day 15: Packages, Interfaces, and Other Features and Day 16: Error Handling and Security |
Review of Session 4 and Short Quiz
Inheritance: When To Use and Not Use It
|
There are two ways to use the Java packaging system: as a consumer and as a producer.
Unlike C++, Java forces you to name and package your files according to specific rules. Each class file has to contain one single public class (file name must be identical to class name with the .java extension). A package is a group of classes, which typically are related.
Example: package mystuff; // the mystuff directory
... scr/mystuff/something.java
The two main packages that ship with the standard JDK are java and javax (for extensions). With all the subpackages, the total package count is approximately 50. The classes inside of each package, typical provide a type of functionality:
To be able to use the classes that a given package provides, you must import those classes by providing their "fully qualified name", which is packagename.classname.
import java.io.InputStream; // import the InputStream class
Note: The fully qualified name avoid collisions between class names, even if you use classes you get from many different sources.
This import statement means that you will be able to use the specified class in your source file.
Notes:
root java io
package name_of_package;
One of the main benefits of using packages is
com.company1.product.Usercom.company2.product.User
There are times when you should,
and should not, use inheritance.
An object hierarchy consists of levels of inheritance.
According to scientific studies, it is easier to remember and understand an
object hierarchy if you keep the number of levels at a minimum (such as five or
fewer).
An example of such a "shallow" hierarchy is this three-level hierarchy for shapes.
Shape | ||||
oval | rectangle | triangle | pentagon | hexagon |
circle | square |
An example of when NOT to use inheritance would be if you were designing a chess game program.
In Java, input and output (I/O) means the movement of data into and out of the Java virtual machine (JVM).
Note: What you type at the keyboard is not a stream of data, but a series of events.
Java uses streams to input and output data. A stream is a flow of data, which goes into the JVM (using the InputStream class), or from the JVM (using the OutputStream class).
For an discussion of streams, see Day 17 (p. 443).
Two key methods of the InputStream class are:
Three key methods of the OutputStream class are:
InputStream and OutputStream are central base classes of the io package.
The io package contains a set of subclasses for InputStream, such as these key subclasses:
Wrapping in Java means to put one object inside of another. It can be used for different purposes, such as putting data in a form that is easier to handle.
Example: To read floats in an efficient manner from a file.
public class MyReadingExample { public static void main(String[] args) { // We create a FileInputStream by passing the name of a file. FileInputStream fis = new FileInputStream("file.txt"); // To enhance the performance we will use a BufferedInputStream // which reads data in blocks. BufferedInputStream bis = new BufferedInputStream(fis); // Wrap the buffered input stream into something that will // converts a series of bytes (or characters from the text // file) into a float value. DataInputStream dis = new DataInputStream(bis); // Here we print a float that we read from the DataInputStream. System.out.println(dis.readFloat()); } }
Note: Wrapping is different from casting or conversion because with wrapping the object does not change. Instead, the object becomes the contents of a different object.
Use Case: You might read in a text file as a data input stream, then use a string tokenizer to parse the data.
Note: Java wraps what would be a character of one byte into unicode, which is two bytes.
Each InputStream can be created from another InputStream. Whenever you call the read method of an InputStream, the read method first calls the read method of the InputStream that it wraps. The wrapped InputStream returns data that the wrapping InputStream handles. Finally, the wrapping InputStream returns its data to the caller.
Note: Reading in a file from the input stream does not directly support random access to the file.
Wrapping allows us to use only four classes (in this example).
If the
Designers of the IO Package had chosen to use inheritance, the number of classes
would increase to over ten because there would multiple levels in the hierarchy.
Let us consider why. If you want to provide the same functionality that those
four classes provide (with the wrapping capabilities), you would have to create
additional classes such as:
You would have to create one class for each possible combination of those different classes to reproduce the whole set of functionality.
Note: You can use methods of the io package's input stream class to perform tasks such as:
Note: System.out.println("Hello World") uses an output stream.
package com.wordesign.util;
import java.io.*;
import java.util.*;
/**
* Title:
First Course In Java: examples and HomeWorks
* Description:
This class is a utility class to read input from the
*
Command Line
*
* Copyright:
Copyright (c) 2001
*
* @author Thomas Albert
* @author Alexandre Aybes
*/
public class ConsoleReader
{
/**
* This method reads an
integer from the command line, it returns only
* once a correct integer has
been entered, if the user enters text that
* cannot be parsed as an
integer, this method will inform the user
* and ask for the data
again.
* @return returns an int
that represents the value entered by the user
*/
public static int readInt()
{
int value = 0;
boolean done = false;
while (!done)
{
try
{
String line = readLine();
// Get the first token by
parsing.
StringTokenizer st = new StringTokenizer(line);
if (st.hasMoreElements())
{
String firstWord = st.nextToken();
value = Integer.parseInt(firstWord);
done = true;
}
}
catch (NumberFormatException nfe)
{
System.err.println("Number Format Exception: "+nfe);
System.out.println("Please enter an integer nuber!");
}
}
return value;
}
/**
*
This method reads a double value from the command line, it returns only
* once a correct double has
been entered, if the user enters text that
* cannot be parsed as a
double, this method will inform the user
* and ask for the data
again.
* @return returns a double
that represents the value entered by the user
*/
public static double readDouble()
{
double value = 0;
boolean done = false;
while (!done)
{
try
{
String line = readLine();
// Get the first token.
StringTokenizer st = new StringTokenizer(line);
if (st.hasMoreElements())
{
String firstWord = st.nextToken();
value = Double.parseDouble(firstWord);
done = true;
}
}
catch (NumberFormatException nfe)
{
System.err.println("Number Format Exception: "+nfe);
System.out.println("Please enter a decimal number!");
}
}
return value;
}
/**
* This method reads a line
of text from the command line, it waits until a
* line of text is entered
(the user has to press return) and then returns
* @return returns the line
the user entered, this method does not return
*
a null value, but might return an empty string ("").
*/
public static String readLine()
{
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
String line = "";
try
{
line = br.readLine();
while (line == null)
{
line = br.readLine();
}
}
catch (IOException ioe)
{
System.err.println("Error reading line: "+ioe);
}
return line;
}
/**
* This is a simple test
method.
*/
public static void main(String[] args)
{
int value = -666;
System.out.print("Enter a number: ");
value = readInt();
System.out.println("You entered: "+value);
System.out.print("Enter a float value: ");
double fp = readDouble();
System.out.println("You entered: "+fp);
}
}
Note: If you are looking for part of a string, you can use methods of the String class.
An "exception" is the programmer's term for an error. An exception means that the program did not run normally and successfully.
The
Java language, like C++ has an exception mechanism built in, but, unlike C++,
the Java languges forces you to use it.
Exceptions
are a way to handle errors and other unexpected conditions. A standard way to
handle those unexpected conditions in programming languages which do not provide
the concept of exceptions would be to have your functions return a code which
corresponds to an error message.
Typically,
0 would be used for 'no error', and anything else for errors:
int status = myErrorProneFunction(); // Check for errors. switch(status) { case 0: // No error. break; case 1: // An error of some kind. showMessageToUser("There was something bad..."); break; case 2: // A error due the user's action. showMessageToUser("You did something... bad."); break; default: // We do not know what caused this error. showMessageToUser("I don't know what happened, but it was bad..."); break; }
This
way of dealing with errors is itself error prone and not really easy to
read.
Therefore, newer programming languages introduced the concept of
Exceptions.
In Java, the Exception object extends the base class Throwable.
An
Exception can be thrown and caught.
When an exception is thrown, program execution:
Stops where the exception occurred.
Jumps to a piece of code that handles this type of exception.
The
Java compiler enforces a rule that there must be exception handling code for any
exception that can be thrown.
Note: It is optional to handle runtime exceptions, such as:
NullPointerException
ArrayOutOfBoundsException - for example, if you refer an array index that does not exist
IllegalArgumentException
- for example if a value is negative when the method only accepts
non-negative values.
Exceptions
are thrown from within a method.
If the exception is not caught in the
same method, it is passed to the caller.
If the caller handles it, the exception stops existing.
If the caller does not handle the
exception, the exception propagates up the call chain to the caller of the
caller.
The exception propagates up the call chain until it is caught.
(Note:
Programmer's sometimes take advantage of this upward propagation because it
provides an opportunity to, in a sense, administrate errors from a more central
place.)
package awtswinguitest;
import java.io.*;
public class ExceptionsTest extends Thread
{
/**
* This method checks if a file exists, and if it does, tries to
read
* from it, but if the file does not exist,
* it will throw a FileNotFoundException,
* which is a subclass of IOException,
* which is itself a subclass of Exception.
*/
public static void exceptionThrowingMethod()
throws IOException
{
if ( !(new File("my_test_file.test").exists())
) // this file has a four-letter extension: test
{
throw new
FileNotFoundException("An example of an exception");
// Code execution halts until the
exception is handled.
}
else
{
// Do something with the file, such
as read the file.
// The method we are using can throw
an IOException,
// so we much either catch it
locally, or declare
// exceptionThrowingMethod() as
throwing IOException
FileInputStream fis = new
FileInputStream("my_test_file.test");
fis.read();
}
}
public static void main(String[] args)
{
// The try block is the part of the code
// that can throw an exception.
// A least one catch block must follow
// each try block, and the catch block
// must catch exactly one type of Exception.
try
{
// The following method is declared
as a
// method that can throw an
IOException.
// This method must catch this
exception
// because the exception cannot be
passed to
// the caller: this method is the
initial method.
exceptionThrowingMethod();
}
catch (FileNotFoundException fnfe)
{
// This catch block only catches
// FileNotFoundException.
// If any other type of Exception
// is thrown, it must be caught
elsewhere.
System.err.println("The file
could not be found.");
}
catch (IOException ioe)
{
// This catch block catches any type
of IOException
// other than FileNotFoundException,
which gets
// caught in the block above.
// If any other type of Exception is
thrown,
// it must be caught elsewhere.
System.err.println("Some sort of
IO Error occurred! "+ioe);
}
catch (Exception e)
{
// Catch the exception and print it
to "standard error" output.
// When we handle the exception, it
does not propagate further.
System.err.println(e);
}
finally
{
// The finally block executes
in all cases:
// (a) no exception was
thrown,
// (b) an exception was
thrown but not caught,
// (c) an exception was
thrown and caught.
System.out.println("We're
done!");
}
}
}
In teams of two persons, study the javadoc of any package in the Java APIs, and prepare a 10 minute presentation of what the package does and how it does it: the main classes, the package organization, and any observations you have about the package.
_________________________
course
homepage course
calendar