T u t o

advertisement
Tutorial 10
Topics
Exception
o Types of Exception
o Catch an exception
o Catch multiple exceptions
o Create your own exception
o Throw an exception
Stream
o Standard input, output, and error
o Input and output streams
o Example
Binary Searching
o Linear search
o Binary search
o Binary tree
Tutorial Problems
Design Problem
Exception
The term exception is shorthand for "exceptional event" which occurs during the
execution of a program that disrupts the normal flow of instructions. When JVM
encounters an exception, it will
stop processing the code it was working on
create a specific kind of exception object
execute the routine that handles such exception
Types of Exceptions
There are two types of Java exceptions checked and unchecked.
Checked exceptions occur because something was wrong in the environment your code
operates in. For example, if your code tries to read from a file that doesn’t exist, the
program will throw an IOException. A checked exception is something beyond your
control - it may happen even though your code is perfectly correct. Therefore, in your
code, you must prepare to handle checked exceptions - you can either catch it or
throws it.
Unchecked exceptions usually occur because you made a programming error. In this
case the best thing you can do is to fix the program. For this reason, you are not
supposed to handle them in your code (you should prevent them in the first place). For
example, if your code tries to read the 10th element of an array of size 9, the program
will throw an ArrayIndexOutOfBoundException.
Catch An Exception
If you think certain parts of your code might generate an exception, enclose that code
inside a try…catch block. Here is the format
try
{ code }
catch (XYException e)
{ handler of XYException }
finally { always execute}
If any of the code inside the try block creates an exception, the program will
skip the remaining codes in the try block and execute the handler code inside
the catch block.
If no exception happens, the program will finish all the codes in the try block
and skip the catch block.
Regardless what happens inside the try block, the codes inside the finally block
will always be executed.
What should you do if you catch an exception? The simplest thing is to print out what
it is. There are three ways you could do it
getMessage() return a string that describes the exception that occurred
toString() return a string made up of the specific exception class name and the error
message
printStackTrace() prints the sequence of method calls that led to the exception on the
standard error stream
Here is an example
public class T10Exception {
int addInputs(String[] args) {
int sum = 0;
try {
for (int i=0; i<args.length; i++)
sum += Integer.parseInt(args[i]);
}
catch (NumberFormatException e) {
System.out.println("\nOutput of getMessage() " + e.getMessage());
System.out.println("Output of toString() " + e.toString());
System.out.println("Output of printStackTrace() " + e.printStackTrace();
}
return sum;
}
public static void main(String[] args) {
T10Exception self = new T10Exception();
String[] test = {"1", "2", "X"};
self.addInputs(test);
}
}
Here are the inputs and outputs
Output of getMessage(): X
Output of toString(): java.lang.NumberFormatException: X
Output of printStackTrace():
java.lang.NumberFormatException: X
at java.lang.Integer.parseInt(Integer.java:414)
at java.lang.Integer.parseInt(Integer.java:463)
at T10Exception.addInputs(T10Exception.java:6)
at T10Exception.main(T10Exception.java:19)
In the above example, when user inputs a non-integer value, the method parseInt
throws an NumberFormatException which is caught by the catch clause and the
program prints out the content of the exception in different formats.Note that
printStackTrace() does not return a string. It will directly print out the message to
stderr.
As covered in lecture, exceptions can inherit from each other. For instance
FileNotFoundException extends IOException so a catch (IOException e ) will also catch
FileNotFoundException. If you are not sure what exception the code segment will
generate, just catch (Exception e), which will catch any exception.
Catch Multiple Exceptions
You can catch multiple exception types in one try…catch block – just use a separate
catch clause for each type as in the following example
try { code }
catch (IOException e1)
{ e1.printStackTrace(); }
catch (NumberFormatException e2)
{ e2.printStackTrace(); }
Create Your Own Exceptions
As with any other Java class, you can extend existing exception classes and create
your own exception type. Here is an example
Class MyException extends NumberFormatException {
public MyException(String msg) {
super("Invalid format " + msg);
}
}
Throw an Exception
In the above example, we caught the NumberFormatException and handled it on the
spot. There might be times when this behavior is not desirable. For example, you are
writing a method for other classes to use which could cause an exception. You might
want to let the user of the class decide how to handle the exception. In this case, you
need to "throw" the exception – passing the handling responsibility to the caller. Here
is how to do it.
Decide what exception your method will cause
Declare the "throws" clause in the method header
Handle the exception in the caller (try/catch)
Here is the modification of the above example
public class T10Exception {
int addInputs(String[] args) throws NumberFormatException {
int sum = 0;
for (int i=0; i<args.length; i++)
sum += Integer.parseInt(args[i]);
return sum;
}
public static void main(String[] args) {
T10Exception self = new T10Exception();
String[] test = {"1", "2", "X"};
try {
self.addInputs(test);
}
catch (NumberFormatException e) {
System.out.println("\nOutput of getMessage() " + e.getMessage());
System.out.println("Output of toString() " + e.toString());
System.out.println("Output of printStackTrace() ");
e.printStackTrace();
}
}
}
In this case, the method addInputs decides not to handle the exception. Instead, it
throws it to the caller. Therefore, the main method have to catch it and handle it.
The output will be the same.
Note that a method can declare unchecked exceptions that it throws but it MUST
declare checked exceptions or the compiler will complain. NumberFormatException is
a good example of an unchecked exception that we DO want to check for. IOException
or FileNotFoundException is a good example of a checked exception that you must
declare.
Streams
So far, our programs have been receiving user inputs only from the keyboard, e.g.
getText() from JTextField. In this section, we will show you how to input data from
and output data to other sources, such as a file or network connection, using stream.
Standard Input, Output, and error
In fact, you have been using streams to send output to the screen since the beginning
of the class
System.out.println(String) prints a string to the standard output stream which
usually (but doesn't have to) means the "screen".
Another common output stream object is the System.err object. The
System.err object allows a program to output error messages. Here again, by
default this output is usually directed to the screen.
System.out and System.err, as well as System.in (which we didn't use much) are
created automatically when a Java program executes. These objects would be
sufficient if you only wanted to write to the screen or read from the keyboard.
Streams
The java.io library allows you to input from and output to other data sources, such as
disks, inter-process pipelines, or network connections. This is accomplished using
stream. Java provides four types of streams
InputStream is an object from with we read a sequence of binary data
Outputstream is an object to which we write a sequence of binary data
Reader is an object from which we read a sequence of text
Writer is an object to which we write a sequence of text
Each stream has a number of subclasses each handle a particular type of data source.
For example
InputStream
o FileInputStream
o ObjectInputStream
o PipedInputStream
OutStream
o FileOutputStream
o ObejctOutputStream
o PipedOutputStream
Once you connect an InputStream to a data source, you can use its read() function to
read data from that source. However, the read() function is pretty limited – it can only
read byte arrays. Most of the time, you need to add a "filter" to translate bytes into
more useful data types. Example of filter input streams include DataInputStream,
BufferedInputStream, etc.
In summary, to read data from a data source, you need to follow the following
procedures
1. Identify the data source (what is it? what type of data does it contain? etc.)
2. Connect an appropriate input stream to that data source (e.g. FileInputStream
or ObjectInputStream)
3. Attach a filter stream to that input stream (e.g. DataInputStream)
4. Read data using methods provided by the filter input stream (e.g. readInt())
The same is true for OutputStreams. Next, we will walk you through an input/output
example which updates employee records based on the number of hours one has
worked for that month. Here are the requirements
Example
There are two files. The first file, Employee_May.dat, contains 5 employee records
with the following format
Name, SSN, hourly rate, salary to date
Here is the content of the file
Wen Xiao, 555-12-3456, 65, 20000
Anil Gupta, 555-22-2222, 70, 30000
Pei-Lei Fan, 555-33-4444, 60, 150000
Katie Chase, 555-44-5555, 80, 40000
The second file, Hours.dat, contains 5 integers, which are the number of hours each
employee has worked for that month. The integers have the same sequence as that of
the employee records. Here is the content of the file
50 60 40 50 70
Our program reads the number of hours worked from Hours.dat, calculates the
monthly salary for that employee, updates her salary to date, and print the new data
to a file called Employee_June.dat
Note that for illustration purposes, we break down the code into relatively
independent blocks. There are certainly more efficient ways to write the program.
Import the Headers
import java.io.*;
import java.util.*;
Read the Data File (Hours.dat)
1. Create a File object representing Hours.dat
2. Connect the file object to an input stream (FileInputStream)
3. Attach a filter stream (DataInputStream) to the input stream
4. Read 5 integers from the data input stream
5. Close the input stream
File f = new File("Hours.dat");
FileInputStream fin= new FileInputStream(f);
DataInputStream din = new DataInputStream(fin);
int[] hours = new int[10];
for (int i=0; i<5; i++)
hours[i] = din.readInt();
din.close();
Read the Text File (Employee_May.dat)
6. Connect Employee_May.dat to a FileReader
7. Attach a BufferedReader to the file reader
8. Read 5 strings from the buffered reader
9. Close the buffered reader
Here, we introduced a new class called BufferedReader. Briefly buffering is a
technique that increases input/output efficiency. Instead of immediately reading from
or writing to the disk each time you request such an operation, the stream object will
use the buffer if it can. For example, if you were writing a series of 256 integers to a
file without buffering, each time you issued an writeInt(int) command, the system
would write to the disk. With buffering, the disk would only be written to when the
buffer was full. BufferedReader also provides the readLine() function which allows use
to read lines, instead of characters.
FileReader fr = new FileReader("Employee_May.dat");
BufferedReader in = new BufferedReader(fr);
String[] records = new String[5];
for (int j=0; j<5; j++)
records[j] = in.readLine();
in.close();
Find the Data
The readLine() function return a string that contains all 4 fields of an employee
record. We need to find the hourly rate and salary to date.
10. Assign the string to a StringTokenizer
11. Find the 3rd and 4th tokens in the string
12. Calculate the salary for this month and add it to the salary to date
In this process, we used a utility class called StirngTokenizer which breaks the string
into individual pieces (tokens) based on the delimiter. In our example, the delimiter is
", "
StringTokenizer st;
String name, ssn;
double hourlyRate, salary;
for(int k=0; k<5; k++) {
st = new StringTokenizer(records[k], ", ");
name = st.nextToken(); ssn = st.nextToken();
hourlyRate = Double.parseDouble(st.nextToken());
salary = Double.parseDouble(st.nextToken());
salary += hourlyRate * hours[k];
records[k] = name + ", " + ssn + ", " + hourlyRate + ", " + salary
}
Note that on the parseDouble() calls, you may want to check for
NumberFormatException
Output the Data
13. Create a new FileWriter with the file name Employee_June.dat
14. Attach a PrintWriter to the file writer
15. Write the string array to the PrintWriter
16. Close the PrintWriter
FileWriter fw = new FileWriter("Employee_June.dat");
PrintWriter out = new PrintWriter(fw);
for (int l=0; l<5; l++)
out.println(records[l]);
out.close();
Binary Searching
The objective of searching is to locate a specific record within a series. There are
several approaches with different efficiencies.
Linear search
Linear search is the sequential process of scanning through the records, starting at the
first record, until either a match is found or the search is completed unsuccessfully.
Linear search is appropriate when the records are in random order or if the data is
stored in a linear linked list. This approach may be reasonable if the size of the series
is small and the content of the series is highly dynamic (e.g. frequent deletion).
Binary search
Binary search is a straightforward procedure for locating an element in an already
sorted array. It follows the "divide and conquer" strategy we used in the bisection
method for root finding. It operates by splitting the data set in half, determining
which half the desired element is in and then cutting it in half again, etc. That is,
binary search is a "divide and conquer" strategy. Binary search is much more efficient
than linear search since it requires less interations. However, before using binary
search, we must sort the data first.
Search with a binary search tree
A binary search tree is a binary tree where the left offspring of any node is "less" than
the root node and the right offspring is "larger" than the root node. To search for an
item in a binary tree, we do the following:
Compare the search key with the root key.
If it is equal, then the search is completed.
Else determine if it is smaller than the root key.
If so, then it must be in the left subtree.
If it is larger, then it must be in the right subtree.
Therefore at each time, around half of the series is eliminated from consideration.
Eventually the procedure finds the key in the tree or comes to a NULL node and
concludes the key is not stored in the tree.
Tutorial Problems
1. Exception
In PS7, we ran into the ArrayStack class whose pop() method throws an exception.
public Object pop() throws EmptyStackException {
if (isEmpty())
throw new EmptyStackException();
else
return stack[top--];
}
Is this a checked exception or unchecked exception?
Why did we decide to throw the exception rather than handle it within the pop
method?
Modify your PS7 program to handle the exception
Design Problem
As mentioned in the tutorial, a stream could be a file or a network connection. In this
exercise, write a small program to read the html file from a website.
Hint: Use URL class instead of File
Download