Session 5 Lecture Notes for First Course in Java  

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

Packaging System

  • import statement
  • Overview of the key packages
  • io, util, applet, awt
  • how to use the API to learn about the packages

Exception Handling 

  • built-in best practices/management
  • a case of inheritance
  • throw, throws
  • try, catch, finally

Input/Output (io package)

  • Use the API to learn about Input/Output
  • FileStream, Buffered Input Stream for a Buffered Reader
  • Why Input/Output does NOT use inheritance

Inheritance: When To Use and Not Use It

  • Good: broad and shallow hierarchies, such as geometric shapes
  • Bad: narrow and deep hierarchies, such as Java’s input/output package

Packaging System

There are two ways to use the Java packaging system: as a consumer and as a producer.

What is a package?

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:

Import statement

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: 

How to create a package

package name_of_package;

Benefits

One of the main benefits of using packages is 

com.company1.product.User
com.company2.product.User

To use the API to learn about the packages (and other things)

See http://java.sun.com/docs

Inheritance: When To Use and Not Use It

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).

Use broad and shallow hierarchies

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.

Input/Output (io package)

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:

Types of input and output

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

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.

How the wrapping works with the InputStream:

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.

Why Input/Output does NOT use inheritance

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.

How the console reader uses an input 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.

Exception Handling 

An "exception" is the programmer's term for an error. An exception means that the program did not run normally and successfully.

Java forces the programmer to handle exceptions

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: 

  1. Stops where the exception occurred.

  2. 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:

The Call Chain

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!");
    }
  }
}

Homework for Sessions 5 and 6

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.

Lecture 5 QUIZ

1.    Why is it better to have a shallow, wide class hierarchy, as opposed to a narrow, deep hierarchy?

It's difficult for people to learn more than three or levels.

2.    What are the benefits of grouping classes into a package?

Easier to find. Less overhead because you have what you need. 

3.    What keyword do you use to make a package available to a class?

"Import". You need the work "package" to create a package. When you import a class, you no longer have to put the fully qualified name of the class.

4.    The io package does not make heavy use of inheritance. What mechanism does the io package use instead?

Wrapping

5.    Name two important base classes of the io package?

InputStream and OutputStream

6.    What is wrapping?

Hiding something by making it only accessible through the object that "wraps" the object or item.

7.    What makes wrapping different from casting?

Wrapping does not convert the data type of the object that gets wrapped.

8.    What package is always available to any Java class you write?

lang   the basic language

9.    The JVM has a class loader that makes specific packages available to the JVM. What would be a disadvantage of making every package in the JDK available to every class you write?

Longer to compile. Slower runtime because Java loads classes dynamically.

10.                    You might buy packages of classes from many different vendors. How can you avoid the problem of class name (name space) collisions?

The package name prepends a fully qualified name that can be specific for a vendor.

11.                    What is a stream of data? What makes it different from a standard datum?

A data stream is read in blocks that might contain, say, five kilobytes of data. A single piece of data, such as the character "l" is static and free-standing.

 

 

_________________________
course homepage      course calendar