Chapter 12 • Exceptions • File I/0 Exceptions • An exception is an object that is generated as a result of an error or an unexpected event. To prevent exceptions from crashing your program, you must write code that detects and handles them. • Exceptions are things that happen at run-time, the Java compiler can’t predict them. In this sense, they are very different from compile errors. Exception handling • One example of an exception would be an attempt to address an array outside its bounds. • Another example would be to try to open a nonexistent file for reading. • This is the code that is triggered when an exception occurs. • The code can be code you write, or it can be default code provided by Java. try - catch • The general form is: try { … … } catch … … } block of code that might generate an exception (ExceptionType e) { block of code to handle the exception Sequence of execution • If an exception occurs in a try block, the next statement executed after the exception is raised is the first statement in the appropriate catch block (there can be more than one of them. More on this later). • If no exception occurs in a try block, all the statements in the catch block are bypassed, and the statement that executes after the last statement in the try block is the first statement after the end of the catch block. Exception types • There is a tree of classes that define exception types. • Different types of exceptions require different handling approaches. For example, you must use the throws keyword for some potential exceptions (unless you handle them), not so for others. • The name “e” is almost always used as the name of the exception object. .getMessage() • This is a method that returns a string that describes the error that tripped the exception. • It is kind of like a .toString() especially for exceptions. • Usually these messages are of limited value to end users. They can be a help for the programmer, though. • This can be handy to use during debugging, but probably isn’t useful in released code. Exceptions – part 2 • Exception Polymorphism – All exceptions can be handled by a clause catch (Exception e) { …} – That’s because all exceptions are extensions of the Exception class. • Sometimes a given try { …} block may generate more than one type of exception, and you may want to handle different exceptions differently. Multiple catch blocks • One try { …} block may be followed by multiple catch (…) {…} blocks. • The order of these blocks is important, because the first one that covers an exception will be executed, and the rest will be ignored. • So, if you are going to have a general, catchall, like catch (exception e) {…}, it must be at the end of the chain. When to use exception handlers. • The end-user should never have to see or deal with a system error! • Even if it is a situation that you can’t fix, you owe the end user an explanation and a way out. • Sometimes we use exception handling to save coding: Go ahead and try to parse into a number (in a try block) . If it won’t parse, maybe it is a different kind of record. The Finally block • After a chain of one or more catch block, you can place a single finally block. • It will always execute, whether or not an exception occurs. • This is especially useful to "clean up" – things like closing files, network stacks, etc. • This may alter the usual flow of instructions in surprising ways – see the following example. The finally clause doFinallyTest(); System.out.println("Back from doFinallyTest"); } static void doFinallyTest() { try { int a = 1/0; } catch (Exception e) { System.out.println("In 'catch' clause"); return; } finally { System.out.println("in 'finally' clause."); } System.out.println("After 'try/catch' block."); } The stack trace • You need to understand what a stack is and how it operates to appreciate this. • There is a big data structure called a stack, and each time a method is called, the variables used in that method go on top of the stack. Returning from a method call involves popping off the stack. • A stack trace gives you the history of all the calls down to the point the exception occurred. Checked, Unchecked, unhandled • All exceptions have to be handled somehow. It is just that some of them are managed by system default handlers. • Exception handling crawls back up the call stack until some method handles it. If you get to the top (in a class) and you don’t handle it, the system will crash your program and handle it. • Unchecked exceptions inherit from Error or RuntimeException class. • The others, checked exceptions, must be handled or have a throws clause in the header. Exceptions – part 3 + some I/O • Throwing Exceptions – throw new exceptiontype(messagestring); – This gives you the opportunity to avoid deep nesting, or using tacky return or break statements. – In many cases, unless you are writing code for other programmers, it might be better to “handle the problem” with your own logic and messages. Creating new exception classes • Extends an exception class • Use super with a new string Input / Output Streams • Reader is an abstract class that other stream reading classes inherit • InputStreamReader extends reader. • FileReader extends InputStreamReader, and can deal with files. FileReader • 3 overloads of read. – No arg returns one char – char[] returns the a bunch of chars array. Trys to return array.length, but may get less. Returns number of chars (or -1) as a function. – char[], int offset, int length returns. Offset gives the starting point to use in the array, length gives the maximum number of characters to read. Returns number of chars (or -1) as a function. • close() – closes the file. BufferedReader • Inherits the three reads from FileReader • Adds: – String readLine()reads up to a line delimiter. Returns null at End of File (EOF). Throws IOException if an error. – long skip(long n) Causes a skip of n characters in the stream. Returns number actually skipped. Sequential I/0 • Let’s begin by building a little example of a program to open and read a file. • Import java.io.* … JFileChooser jfc = new JFileChooser(); status = jfc.showOpenDialog(null); inputFile = jfc.getSelectedFile(); inputFileName = inputFile.getPath(); FileReader fr = new FileReader(inputFileName); BufferedReader inF = new BufferedReader(fr); Sequential Output • classes – Writer - abstract class for other stream writing classes – OutputStreamWriter extends Writer to format an output stream of data as characters. – FileWriter extends OutputStreamWriter to be able to write text to file. FileWriter methods • void write(int c) writes a single character • void write(char[] array) writes an array of characters. • void write(char[] array, int offset, int length) writes a piece of the array, starting at offset, for length characters. • void write(String str) writes the string. • void write(String str, int offset, int length) writes a piece of the str, starting at offset, for length characters. • void close() closes the file import java.io.FileWriter; import javax.swing.JFileChooser; public class FileWriterDemo { public static void main(String[] args) { JFileChooser fc = new JFileChooser(); int status = fc.showSaveDialog(null); if (status != JFileChooser.APPROVE_OPTION) return; try { FileWriter fw = new FileWriter(fc.getSelectedFile()); fw.write('x'); fw.write("\r\n"); fw.close(); System.out.println("Done"); } catch (Exception e){ System.out.println("FileWriter I/O failed."); return; } } } 12.4 Advanced topics • Binary Files • Random Access Files • Object Serialization Binary Files • The files we’ve dealt with so far are ASCII (?Unicode) text files. • Numbers are better stored as the bits they are, rather than formatting to strings and then parsing back to numbers. – Accuracy issues – Speed issues • Binary files just store the bits and bytes “as they are” Classes for Binary I/O • FileOutputStream (in java.io) Bytes only. • DataOutputStream provides methods for any primitive data type or Strings. Needs to work with FileOutputStream. • DataOutputStream myfile = new DataOutputStream( new FileOutputStream(myFilespecString)); Methods in DataOutputStream • void close() duh, closes file • void writeType(type variableName) – boolean, byte, char, int, double, float, int, long, short – e.g. void writeBoolean(boolean b) • void writeUTF(String str) note, not “string” Reading Binary Data • FileInputStream and DataInputStream – As for writing, bytes only on former, types on latter. • DataInputStream myInputFile = new DataInputStream(new FileInputStream(myFilespec)); DataInputStream methods • void close () duh • type readType() for types: – Byte, char, double, float, int, long, short – e.g. char readChar() • String readUTF() • Appending – FileOutputStream takes a second argument, Boolean. If true, the file will not be erased, and new data will be appended. – So does FileWriter for text output. Random Access Files • RandomAccessFile(String filename, String mode); • mode values: “r” – read, “rw” for read/write – If you open in “r” it must exist or exception thrown. – If you open in “r” mode and try to write: exception thrown – If you open in “rw” and it exists, contents are preserved – if you open in “rw” and it doesn’t exist, it gets created. • Has the same methods as DataOutputStream and DataInputSteam Serial I/O in random file • • • • • There is a File Pointer, initially 0. Writes in overstrike mode, not insert mode. Read past end throws EOFException Write past end extends the file. File pointer adjusted with void seek(long position) Object Serialization • Serializable interface • import java.io.Serializable; • ObjectOutputStream and ObjectInputStream classes • writeObject and ReadObject methods • If a class implements Serializable interface, all fields must be Serializable. All primitives are, and Strings are too. • transient keyword causes nonserializable fields to be skipped. This may take experimentation.