Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Chapter 14: Files Chapter 14 Files Java Programming FROM THE BEGINNING 1 Chapter 14: Files 14.1 Files and Streams • A file is a collection of related data that is given a name and placed on a storage medium. • A file can be a place to keep data for a long period of time, or it can be used for temporary storage. • Files can be stored on a variety of media, including hard drives, floppy disks, CD-ROMs, and tapes. • Files can also be transmitted in various ways, such as over a computer network. • All files are the same in one important respect: they consist of bytes. Java Programming FROM THE BEGINNING 2 Chapter 14: Files How Files Are Stored • One way to see the sizes of files is to use the dir command in Windows: Volume in drive C has no label Volume Serial Number is 07CE-0C08 Directory of C:\programs\lottery . <DIR> .. <DIR> LOTTER~1 JAV LOTTER~1 CLA 2 file(s) 2 dir(s) 12-20-99 1:02p 12-20-99 1:02p 283 12-20-99 1:04p 511 12-20-99 1:04p 794 bytes 12,802.72 MB free . .. Lottery.java Lottery.class • The Lottery.java file contains 283 bytes, and the Lottery.class file contains 511 bytes. Java Programming FROM THE BEGINNING 3 Chapter 14: Files How Files Are Stored • In some files, each byte represents a character. In other files, the bytes mean other things. • Suppose that a file consists of four bytes: • Some possible meanings: – The bytes represent the ASCII characters J, a, v, and a. – The bytes represent a single integer. – The first two bytes represent a short integer, and the second two represent another short integer. – The bytes represent a single float value. Java Programming FROM THE BEGINNING 4 Chapter 14: Files How Files Are Stored • Ways to leave hints about the contents of a file: – The file’s extension – A “marker” in the file itself, usually at the beginning • In Windows, an executable file begins with two special bytes containing the ASCII codes for the letters M and Z. • In Unix, an executable file begins with a “magic number.” When the user tries to execute a program, Unix first checks for the magic number. Java Programming FROM THE BEGINNING 5 Chapter 14: Files Text Files Versus Binary Files • Files fall into two categories: – In a text file, the bytes represent characters in some character set, such as ASCII or Unicode. – In a binary file, the bytes don’t necessarily represent characters (although some of them may). Java Programming FROM THE BEGINNING 6 Chapter 14: Files Text Files Versus Binary Files • Text files have two characteristics that binary files don’t possess. • First, text files are divided into lines. • In Windows, the end-of-line marker is a carriagereturn character ('\r') followed immediately by a line-feed character ('\n'). • In Unix, the end-of-line marker is a single linefeed character. • The Macintosh operating system uses a single carriage-return character. Java Programming FROM THE BEGINNING 7 Chapter 14: Files Text Files Versus Binary Files • Second, text files may contain a special “end-offile” marker. • In Windows, the marker is '\u1a' (Ctrl-Z). CtrlZ doesn’t have to be present, but if it is, it marks the end of the file. • In a binary file, there’s no “end-of-line” or “endof-file” marker; all bytes are treated equally. Java Programming FROM THE BEGINNING 8 Chapter 14: Files Text Files Versus Binary Files • Suppose that a Windows text file contains the following lines: Java rules! • The bytes in the file, shown as hexadecimal codes: Java Programming FROM THE BEGINNING 9 Chapter 14: Files Streams • Programs that work with files will need to use the java.io package. • The names of many java.io classes include the word “stream.” • A stream is an abstraction that represents any “file-like” source of input or destination for output. • A stream object may be capable of reading from anything that resembles a file or writing to anything that resembles a file. Java Programming FROM THE BEGINNING 10 Chapter 14: Files Streams • Classes whose names end with Stream are subclasses of InputStream and OutputStream: • System.out and System.err are instances of the PrintStream class. Java Programming FROM THE BEGINNING 11 Chapter 14: Files Streams • Other classes have names that end with Reader or Writer. • These classes are subclasses of Reader and Writer: Java Programming FROM THE BEGINNING 12 Chapter 14: Files Streams • The reader and writer classes are designed to help with a common problem: Java stores characters in Unicode, whereas most software assumes that characters are stored in ASCII form. • An instance of a Reader class solves the problem by automatically converting bytes to Unicode characters during input. • Similarly, an instance of a Writer class will convert Unicode characters to single bytes during output. Java Programming FROM THE BEGINNING 13 Chapter 14: Files Stream Layering • The first step in working with a file is creating an object that represents the file. • Operations can then be performed on the file by calling instance methods. • Many of the classes java.io are designed to be “layered,” with two or more objects involved. • Calling a method that belongs to one object triggers a call to an “underlying” object, which may in turn call a method that belongs to a third object. Java Programming FROM THE BEGINNING 14 Chapter 14: Files Stream Layering • For example, to write characters to a file, an instance of the FileWriter class will be needed. • This class isn’t very efficient by itself, so a BufferedWriter object will need to be layered on top of the FileWriter object. • The methods in the BufferedWriter class aren’t very convenient to use, however, so a PrintWriter object will need to be layered on top of the BufferedWriter object. Java Programming FROM THE BEGINNING 15 Chapter 14: Files Stream Layering • A diagram showing the layering of the three classes: Java Programming FROM THE BEGINNING 16 Chapter 14: Files Stream Layering • Calling a method that belongs to PrintWriter in turn calls a method (or methods) belonging to BufferedWriter, which then calls a method(s) belonging to FileWriter. • Layering provides flexibility: the programmer can combine classes in many different ways to get the right blend of efficiency and convenience. Java Programming FROM THE BEGINNING 17 Chapter 14: Files Working with Files • Working with a file involves three steps: – Open the file – Perform operations on the file – Close the file Java Programming FROM THE BEGINNING 18 Chapter 14: Files Opening a File • Opening a file requires specifying the file’s name and possibly its location. • Opening a file is done by creating an instance of an appropriate stream class (or—in the case of a text file—a reader or writer class). • This object represents the file within the program. • Creating a FileOutputStream object opens a binary file for writing: FileOutputStream out = new FileOutputStream(filename); Java Programming FROM THE BEGINNING 19 Chapter 14: Files Opening a File • Opening a file for input will fail—causing an exception to be thrown—unless the file already exists. • When an existing file is opened for output, the file is normally truncated—the bytes already in the file are lost. • Attempting to open a nonexistent file for output will cause the file to be created. Either way, the file is empty to begin with. Java Programming FROM THE BEGINNING 20 Chapter 14: Files Performing Operations on a File • File operations, such as reading data or writing data, are performed by calling instance methods that belong to the stream object’s class. • The operations that can be performed on the out object are dictated by the methods in FileOutputStream (and its superclasses). • One of these methods, named write, is capable of writing a single byte: out.write(b); Java Programming FROM THE BEGINNING // Writes the byte b 21 Chapter 14: Files Closing a File • A file should be closed when no more operations will be performed on it. • Closing a file is done by calling the close method: out.close(); • All stream, reader, and writer classes support the close method (either by providing it or inheriting it). Java Programming FROM THE BEGINNING 22 Chapter 14: Files Obtaining File Names • Ways to supply a file name to a program: – Build the name into the program – Prompt the user to enter the name – Obtain the name from the command line • An example of obtaining file names from the command line: java CopyFile file1 file2 • args[0] will contain the name of the file to be copied. args[1] will contain the name of the file that will store the copy. Java Programming FROM THE BEGINNING 23 Chapter 14: Files Buffering and Flushing • A buffer is a place where data is stored temporarily, usually on its way from one place to another. • Buffers are common when working with files. • When a program calls a method that writes to a file, the data being written usually goes into a buffer first. • When the buffer is full, all the data in the buffer is then transferred to the file, and the buffer becomes empty. Java Programming FROM THE BEGINNING 24 Chapter 14: Files Buffering and Flushing • Similar actions take place when data is read from a file: a large chunk of data is first transferred to a buffer. • The program then obtains small portions of the data from the buffer. • When the buffer becomes empty, another large chunk is read from the file. Java Programming FROM THE BEGINNING 25 Chapter 14: Files Buffering and Flushing • Using buffers for input and output can dramatically improve a program’s performance. • Transferring information to or from a disk drive is a relatively slow operation. • Reading a byte from a buffer or storing a byte into a buffer takes hardly any time at all. • It takes time to transfer the buffer contents to or from disk, but one large data transfer is much faster than many small ones. Java Programming FROM THE BEGINNING 26 Chapter 14: Files Buffering and Flushing • Java’s stream classes are designed to perform buffering without any action on the programmer’s part. • Occasionally, though, it’s necessary to take a more active role. • Normally, data written to a file goes into a buffer first. • The buffer is flushed (written to the file) automatically when it’s full or the file is closed. Java Programming FROM THE BEGINNING 27 Chapter 14: Files Buffering and Flushing • The buffer can be flushed manually by calling the flush method: out.flush(); out can be an output stream or writer object • Calling flush ensures that data is written to a file as soon as possible, where it will be safe. • All output stream and writer classes support the flush method. Java Programming FROM THE BEGINNING 28 Chapter 14: Files File Pointers • For each open file, Java maintains a file pointer, which keeps track of which byte will be the next to be read or written. • If a file is opened for reading, the file pointer will be at the beginning: Java Programming FROM THE BEGINNING 29 Chapter 14: Files File Pointers • If one byte is now read from the file, the file pointer will advance automatically to the next byte: • The file pointer will eventually advance past the last byte in the file—a condition known as end of file. • It’s important to test for this condition each time input is read from a file. Java Programming FROM THE BEGINNING 30 Chapter 14: Files File Pointers • Writing to a file is similar: Java keeps track of the current position in the file and advances the file pointer after each write operation. • Normally, writing starts at the beginning of a file. • In some cases, however, it may be necessary to append data to the end of the file, thereby preserving the file’s original contents. Java Programming FROM THE BEGINNING 31 Chapter 14: Files File Pointers • Reading or writing a file from beginning to end is called sequential access. • Java also supports a different type of access, known as random access. • Random access allows a program to move freely within a file. • Random access requires the use of the RandomAccessFile class. Java Programming FROM THE BEGINNING 32 Chapter 14: Files 14.2 The File Class • File is one of the most fundamental classes in the java.io package. • A File object represents a file stored on disk or some other medium. • The File class allows a program to work with the properties of a file, not the contents of the file. • File also provides a few basic file operations, including deleting a file and renaming a file. Java Programming FROM THE BEGINNING 33 Chapter 14: Files Creating File Objects • There are several ways to create a File object. • One File constructor expects a string containing the file’s name: File f = new File("Lottery.java"); Java will try to locate the file in the current directory. • To work with a file in a different directory, path information will need to be included in the string: File f = new File("c:\\programs\\lottery\\Lottery.java"); Java Programming FROM THE BEGINNING 34 Chapter 14: Files Creating File Objects • The path and the file name can be supplied as separate arguments: File f = new File("c:\\programs\\lottery", "Lottery.java"); • In Windows, two backslash characters are needed to separate directory names. • Java would interpret a single backslash character as the beginning of an escape sequence. Java Programming FROM THE BEGINNING 35 Chapter 14: Files Creating File Objects • Another way to solve the backslash problem is to use a slash character instead of a backslash to separate directory names: File f = new File("c:/programs/lottery/Lottery.java"); • Although the backslash is the normal character for separating directory names in Windows, ordinary slashes will also work within Java programs. Java Programming FROM THE BEGINNING 36 Chapter 14: Files File Properties • The File class provides a set of “query” methods that return information about a file: Description Action boolean canRead() Returns true if the program can read from this file. boolean canWrite() Returns true if the program can write to this file. boolean exists() Returns true if this file exists. boolean isDirectory() Returns true if this file is a directory. boolean isFile() Returns true if this file is normal (not a directory). long length() Returns the length of this file in bytes. String[] list() Returns an array of strings containing the names of the files in this directory. Java Programming FROM THE BEGINNING 37 Chapter 14: Files Program: Determining the Properties of a File • The FileProperties program prompts the user for the name of a file and then displays the file’s properties. • Output of the program when asked to show the properties of the Lottery.java file: Enter a file name: Lottery.java File can be read File can be written File exists File is normal Length of file: 283 Java Programming FROM THE BEGINNING 38 Chapter 14: Files Program Behavior • Output when asked to show the properties of the directory containing Lottery.java: Enter a file name: c:\programs\lottery File can be read File can be written File exists File is a directory Length of file: 0 Java Programming FROM THE BEGINNING 39 Chapter 14: Files FileProperties.java // Displays the properties of a file import java.io.*; import jpb.*; public class FileProperties { public static void main(String[] args) { // Prompt user to enter file name SimpleIO.prompt("Enter a file name: "); String fileName = SimpleIO.readLine(); // Create a File object File f = new File(fileName); Java Programming FROM THE BEGINNING 40 Chapter 14: Files // Display properties of file if (f.canRead()) System.out.println("File can be read"); if (f.canWrite()) System.out.println("File can be written"); if (f.exists()) System.out.println("File exists"); if (f.isDirectory()) System.out.println("File is a directory"); if (f.isFile()) System.out.println("File is normal"); System.out.println("Length of file: " + f.length()); } } Java Programming FROM THE BEGINNING 41 Chapter 14: Files Program: Listing the Files in a Directory • The list method returns an array containing the names of all files in a particular directory. • The ListFiles program uses list to display the names of the files in the current directory (represented by the file name "."). • Output of the program when used to list the files in the c:\programs\lottery directory: Lottery.java Lottery.class Java Programming FROM THE BEGINNING 42 Chapter 14: Files ListFiles.java // Displays a list of all files in the current directory import java.io.*; public class ListFiles { public static void main(String[] args) { // Obtain a list of all files in the current directory File currentDirectory = new File("."); String[] fileNames = currentDirectory.list(); // Display each name in the list for (int i = 0; i < fileNames.length; i++) System.out.println(fileNames[i]); } } Java Programming FROM THE BEGINNING 43 Chapter 14: Files File Operations • In addition to query methods, the File class also provides a few basic operations on files: Description Action boolean delete() Deletes this file. boolean mkdir() Creates a directory corresponding to this File object. boolean renameTo(File dest) Renames this file to the name specified by dest. • All three methods return true if they are successful and false if they are not successful. Java Programming FROM THE BEGINNING 44 Chapter 14: Files Program: Renaming the Files in a Directory • The RenameFiles program displays the names of all files in the current directory, one by one, and allows the user to change the names of selected files. • A sample session with the program: Rename Lottery.java (y/n)? n Rename Lottery.class (y/n)? y Enter new name: Lottery2.class Java Programming FROM THE BEGINNING 45 Chapter 14: Files RenameFiles.java // Renames selected files in the current directory import java.io.*; import jpb.*; public class RenameFiles { public static void main(String[] args) { // Obtain a list of all files in the current directory File currentDirectory = new File("."); String[] fileNames = currentDirectory.list(); // Process each name in the list for (int i = 0; i < fileNames.length; i++) { // Ask user whether or not to rename file SimpleIO.prompt("Rename " + fileNames[i] + " (y/n)? "); String response = SimpleIO.readLine(); Java Programming FROM THE BEGINNING 46 Chapter 14: Files // If the answer is "y" or "Y", ask for new name and // then call renameTo if (response.equalsIgnoreCase("y")) { SimpleIO.prompt("Enter new name: "); String newName = SimpleIO.readLine(); File oldFile = new File(fileNames[i]); File newFile = new File(newName); boolean successful = oldFile.renameTo(newFile); if (!successful) System.out.println("Could not rename " + fileNames[i] + " to " + newName); } } } } Java Programming FROM THE BEGINNING 47 Chapter 14: Files 14.3 Reading and Writing Bytes • The FileInputStream and FileOutputStream classes are used to read and write single bytes or blocks of bytes. • These classes are useful for writing file utilities, such as a program that makes a copy of a file. Java Programming FROM THE BEGINNING 48 Chapter 14: Files Writing Bytes • Opening a file for writing is done by creating a FileOutputStream object. • Common FileOutputStream constructors: Description FileOutputStream( String name) FileOutputStream( String name, boolean append) FileOutputStream( File file) Java Programming FROM THE BEGINNING Action Creates a FileOutputStream object representing the file with the specified name. Creates a FileOutputStream object representing the file with the specified name. If append is true, bytes will be written to the end of the file rather than the beginning. Creates a FileOutputStream object representing the specified File object. 49 Chapter 14: Files Writing Bytes • The first constructor needs a file name (possibly containing path information) as its argument: FileOutputStream out = new FileOutputStream(fileName); • The second constructor is used to specify that any bytes written to the file should be appended to the file, thereby preserving the file’s original contents. • The third constructor takes a File object instead of a file name. Java Programming FROM THE BEGINNING 50 Chapter 14: Files Writing Bytes • Using the FileOutputStream constructor to open an existing file causes the file to be truncated. • The contents of the file is preserved, however, if the second constructor is used and the value of append is true. • If the FileOutputStream constructor is given the name of a file that doesn’t exist, the file will be created. Java Programming FROM THE BEGINNING 51 Chapter 14: Files Writing Bytes • If a file can’t be opened for writing, the FileOutputStream constructor will throw IOException. • Because IOException is a checked exception, the constructor call will need to be enclosed within a try block. • Starting with version 1.2 of the JDK, the FileOutputStream constructor throws FileNotFoundException instead of IOException. Java Programming FROM THE BEGINNING 52 Chapter 14: Files Writing Bytes • The primary methods provided by FileOutputStream are all named write. • These methods throw IOException if an error occurs. • The first version of write writes a single byte: byte outputByte; … out.write(outputByte); • The second version writes a block of bytes stored in an array: byte[] buffer = new byte[512]; … out.write(buffer); Java Programming FROM THE BEGINNING 53 Chapter 14: Files Writing Bytes • The third version is also designed to write a block of bytes. It requires two additional arguments: byte[] buffer = new byte[512]; int count; … out.write(buffer, 0, count); • The second argument is an offset into the array; the value 0 indicates that the byte at position 0 will be the first to be written. • The third argument is the number of bytes to write. Java Programming FROM THE BEGINNING 54 Chapter 14: Files Writing Bytes • Most operations on a FileOutputStream object can throw IOException. • An attempt to open a file for writing might fail because of an invalid path, no permission to write to the file, and so on. • Attempts to write to the file could fail because the physical device has a problem or because the device is full. Java Programming FROM THE BEGINNING 55 Chapter 14: Files Writing Bytes • A program that works with files should specify what action should be taken for each potential failure. • Because IOException is a checked exception, possible errors can’t simply be ignored. • In many cases, the program won’t be able to continue execution, but it should at least display a meaningful error message. • In particular, it’s a good idea to display the name of the file that has caused the problem. Java Programming FROM THE BEGINNING 56 Chapter 14: Files Reading Bytes • Reading bytes from a file requires the creation of a FileInputStream object. • Common FileInputStream constructors: Description Action FileInputStream( Creates a FileInputStream object String name) representing the file with the specified name. FileInputStream( Creates a FileInputStream object File file) representing the specified File object. • The first constructor requires a file name: FileInputStream in = new FileInputStream(fileName); • Both constructors throw FileNotFoundException if the specified file can’t be opened for input. Java Programming FROM THE BEGINNING 57 Chapter 14: Files Reading Bytes • The primary methods provided by FileInputStream are all named read: Description Action int read() Reads a byte, returning it in int form. int read(byte[] b) Reads bytes into b until the array is full or the end of the file has been reached. Returns the number of bytes read. int read(byte[] b, Reads up to len bytes into the array b, int off, starting at the position indicated by off. int len) Returns the number of bytes read. • All three methods throw IOException if an error occurs. • If any read method fails to read any input because it has reached the end of the file, it returns –1. Java Programming FROM THE BEGINNING 58 Chapter 14: Files Reading Bytes • The first version of the read method reads a single byte: int byteValue = in.read(); • read returns an int value, not a byte value, so the return value should be stored in an int variable. • The variable should then be tested to see if it is equal to –1 (indicating that no byte could be read). • If the variable is not equal to –1, its value can then be cast to byte form. Java Programming FROM THE BEGINNING 59 Chapter 14: Files Reading Bytes • A loop that reads all the bytes in a file: while (true) { int byteValue = in.read(); if (byteValue == -1) break; // End of file reached byte inputByte = (byte) byteValue; … } • In some cases, it’s possible to omit the cast and just leave the byte in the original int variable. Java Programming FROM THE BEGINNING 60 Chapter 14: Files Reading Bytes • The second version of read reads bytes into an array until it’s full or no more bytes can be read: byte[] buffer = new byte[512]; … int count = in.read(buffer); • The third version specifies where the bytes should be stored in the array and how many bytes can be read. • After calling either version of read, it’s important to test whether count has the value –1. • If so, no bytes could be read because the method encountered the end of the file. Java Programming FROM THE BEGINNING 61 Chapter 14: Files Program: Copying a File • The CopyFile program will copy a file. • The FileInputStream and FileOutputStream classes are perfect for this kind of program, which has no knowledge of what the original file contains. • The user will supply the names of the original file and the new file on the command line: java CopyFile source destination source is the name of the original file, and destination is the name of the new file. Java Programming FROM THE BEGINNING 62 Chapter 14: Files Design of the CopyFile Program • Strategy: – Check that there are two command-line arguments. – Create a FileInputStream object to represent the source file. – Create a FileOutputStream object to represent the destination file. – Use a loop to read a block of bytes from the FileInputStream object and write the block to the FileOutputStream object. – Close both files. Java Programming FROM THE BEGINNING 63 Chapter 14: Files Exceptions in the CopyFile Program • Checked exceptions that can occur: – The FileInputStream constructor can throw FileNotFoundException. – The FileOutputStream constructor can throw IOException (or FileNotFoundException). – The read method in FileInputStream can throw IOException. – The write method in FileOutputStream can throw IOException. – The close methods in FileInputStream and FileOutputStream can throw IOException. Java Programming FROM THE BEGINNING 64 Chapter 14: Files Exceptions in the CopyFile Program • Instead of having a separate try block and catch block for each exception, most of the program will be enclosed within a single try block. • After the try block will come two catch blocks, one for FileNotFoundException and one for IOException. Java Programming FROM THE BEGINNING 65 Chapter 14: Files CopyFile.java // Copies one file into another. The names of both files must // be specified on the command line. import java.io.*; public class CopyFile { public static void main(String[] args) { // Terminate program if number of command-line arguments // is wrong if (args.length != 2) { System.out.println("Usage: java CopyFile source dest"); System.exit(-1); } Java Programming FROM THE BEGINNING 66 Chapter 14: Files try { // Open source file for input and destination file for // output FileInputStream source = new FileInputStream(args[0]); FileOutputStream dest = new FileOutputStream(args[1]); // Set up a 512-byte buffer byte[] buffer = new byte[512]; // Copy bytes from the source file to the destination // file, 512 bytes at a time while (true) { int count = source.read(buffer); if (count == -1) break; dest.write(buffer, 0, count); } Java Programming FROM THE BEGINNING 67 Chapter 14: Files // Close source and destination files source.close(); dest.close(); } catch (FileNotFoundException e) { System.out.println("File cannot be opened"); } catch (IOException e) { System.out.println("I/O error during copy"); } } } Java Programming FROM THE BEGINNING 68 Chapter 14: Files 14.4 Advanced Exception-Handling • The constructors and methods of the stream classes throw a variety of checked exceptions. • Section 8.1 covered the rudiments of exceptionhandling, but a more detailed knowledge is needed in order to write programs that use files. Java Programming FROM THE BEGINNING 69 Chapter 14: Files The Hierarchy of Exception Classes • For each kind of exception, the Java API provides a corresponding class, such as IOException. • A class named Throwable is the superclass (directly or indirectly) for all exception classes. • The Throwable class has only two direct subclasses: Error and Exception. • Classes that represent specific kinds of exceptions are subclasses of either Error or Exception. Java Programming FROM THE BEGINNING 70 Chapter 14: Files The Hierarchy of Exception Classes • A diagram showing how the exception classes are descended from Throwable: • This diagram shows only a few of Throwable’s many subclasses. Java Programming FROM THE BEGINNING 71 Chapter 14: Files The Hierarchy of Exception Classes • Exception classes that extend Error represent unrecoverable errors, such as the Java interpreter failing to load a class or running out of memory. • Classes that extend Exception represent problems that can potentially be handled within a program. • Subclasses of Error or RuntimeException represent unchecked exceptions. • Subclasses of Exception (but not RuntimeException) represent checked exceptions. Java Programming FROM THE BEGINNING 72 Chapter 14: Files Using Multiple catch Blocks • The fact that exception classes are related to each other has some interesting consequences for writing catch blocks. • Suppose that a try block is followed by two or more catch blocks: try block catch (exception-type identifier) block … catch (exception-type identifier) block Java Programming FROM THE BEGINNING 73 Chapter 14: Files Using Multiple catch Blocks • If an exception is thrown inside a try block, the first catch block with a compatible exception type will be allowed to handle the exception. • A catch block can catch exceptions of a specified type, as well as exceptions that belong to a subclass of that type. • For example, a catch block for IOException can also catch FileNotFoundException, which is a subclass of IOException. Java Programming FROM THE BEGINNING 74 Chapter 14: Files Using Multiple catch Blocks • More than one catch block may be capable of handling a given exception: try { … } catch (FileNotFoundException e) { … } catch (IOException e) { … } • If a FileNotFoundException occurs, both catch blocks can potentially handle the exception, but only the first will be allowed to. Java Programming FROM THE BEGINNING 75 Chapter 14: Files Using Multiple catch Blocks • If catch blocks are put in the wrong order, it’s possible that an early catch block may end up catching exceptions that were meant for a later catch block: try { … } catch … // } catch … // } (IOException e) { Catches FileNotFoundException as well (FileNotFoundException e) { Never executed! • The second catch block can never be executed; if a FileNotFoundException occurs in the try block, the first catch block will handle the exception. Java Programming FROM THE BEGINNING 76 Chapter 14: Files Using Multiple catch Blocks • A catch block for IOException will catch all exceptions that belong to subclasses of IOException. • Using such a catch block isn’t always appropriate, because the program can no longer act differently based on the particular exception. • If a single catch block is used to catch I/O exceptions, it’s a good idea to have it display the message stored inside the IOException object. Java Programming FROM THE BEGINNING 77 Chapter 14: Files Using Multiple catch Blocks • It’s possible to write catch blocks that handle a wide variety of exceptions, not just I/O exceptions. • A catch block for Exception will catch any exception that represents a recoverable error: try { … } catch … // } catch … // } (IOException e) { Catches all I/O exceptions (Exception e) { Catches all other exceptions Java Programming FROM THE BEGINNING 78 Chapter 14: Files Using Multiple catch Blocks • Using Exception in a catch block can be dangerous, however: the block may catch exceptions that weren’t anticipated when the program was designed. • Whenever possible, use specific exception classes in catch blocks, rather than broader classes such as Exception. Java Programming FROM THE BEGINNING 79 Chapter 14: Files finally Blocks • After the last in a series of catch blocks, a finally block may be present: try block catch (exception-type identifier) block … catch (exception-type identifier) block finally block Java Programming FROM THE BEGINNING 80 Chapter 14: Files finally Blocks • The finally block is executed after the try block terminates or the exception is caught by a catch block. • It must be executed even if the try block terminates prematurely because of a control statement (break, continue, or return). • Typically, the finally block contains code that closes files or performs other “cleanup” actions. • finally can even be used without any catch blocks. Java Programming FROM THE BEGINNING 81 Chapter 14: Files The throws Clause • Actually, it’s not always the case that checked exceptions must be caught. • When there’s a possibility that a checked exception might be thrown inside a method, there are two choices: – Handle the exception within the method. – Declare that the method throws the exception. Any method that calls this one will now be responsible for handling the exception. Java Programming FROM THE BEGINNING 82 Chapter 14: Files The throws Clause • Consider the problem of writing a helper method that calls Java’s Thread.sleep method. • Thread.sleep throws a checked exception named InterruptedException. • One approach is to handle the exception within the new method: static void sleep(int time) { try { Thread.sleep(time); } catch (InterruptedException e) {} } Java Programming FROM THE BEGINNING 83 Chapter 14: Files The throws Clause • The other possibility is to declare that the new method throws InterruptedException: static void sleep(int time) throws InterruptedException { Thread.sleep(time); } • If InterruptedException occurs, the new method will return. The method that called it will now be responsible for catching the exception. • Of course, that method might also have a throws clause. Java Programming FROM THE BEGINNING 84 Chapter 14: Files The throws Clause • If a method fails to either catch a checked exception or declare it using throws, the compiler will issue an error message. • Which technique is best depends on whether or not it is meaningful to deal with the exception inside the method where it’s thrown. • If so, then there’s no reason for the calling method to know about the exception. • Otherwise, it’s better to let the calling method handle the exception. Java Programming FROM THE BEGINNING 85 Chapter 14: Files Program: Copying a File (Revisited) • The throws clause is sometimes used by programmers looking for a shortcut. • The CopyFile2 program is similar to the earlier CopyFile program, except that it does not handle exceptions. • Instead, CopyFile2 declares that main throws IOException. • There’s no need to mention that main throws FileNotFoundException, because FileNotFoundException is a subclass of IOException. Java Programming FROM THE BEGINNING 86 Chapter 14: Files Program Behavior • If an IOException (or FileNotFoundException) occurs during the execution of CopyFile2, it will terminate with an error message about an unhandled exception. • From a design standpoint, it’s better to handle the exception within the program itself, displaying an appropriate error message and, if possible, taking steps to recover from the exception. Java Programming FROM THE BEGINNING 87 Chapter 14: Files CopyFile2.java // Copies one file into another. The names of both files must // be specified on the command line. import java.io.*; public class CopyFile2 { public static void main(String[] args) throws IOException { // Terminate program if number of command-line arguments // is wrong if (args.length != 2) { System.out.println("Usage: java CopyFile2 source dest"); System.exit(-1); } // Open source file for input and destination file for // output FileInputStream source = new FileInputStream(args[0]); FileOutputStream dest = new FileOutputStream(args[1]); Java Programming FROM THE BEGINNING 88 Chapter 14: Files // Set up a 512-byte buffer byte[] buffer = new byte[512]; // Copy bytes from the source file to the destination // file, 512 bytes at a time while (true) { int count = source.read(buffer); if (count == -1) break; dest.write(buffer, 0, count); } // Close source and destination files source.close(); dest.close(); } } Java Programming FROM THE BEGINNING 89 Chapter 14: Files 14.5 Reading and Writing Data Types • The FileInputStream and FileOutputStream classes are not very convenient for reading and writing bytes that represent specific types of data. • The DataInputStream and DataOutputStream classes are better suited for this task. • These classes are designed for use with binary files. • Numbers and other types of data can be written to a text file, but only after being converted to character form. Java Programming FROM THE BEGINNING 90 Chapter 14: Files Writing Data Types • A DataOutputStream object can’t be created directly from a file name. • Instead, a FileOutputStream object is first created, and then that object is used to create a DataOutputStream object: FileOutputStream fileOut = new FileOutputStream(fileName); DataOutputStream out = new DataOutputStream(fileOut); • The DataOutputStream constructor will accept any output stream object as its argument. Java Programming FROM THE BEGINNING 91 Chapter 14: Files Writing Data Types • To shorten the code, some programmers nest the FileOutputStream constructor inside the DataOutputStream constructor: DataOutputStream out = new DataOutputStream( new FileOutputStream(fileName)); • Operations performed on the DataOutputStream object will indirectly affect the underlying FileOutputStream object. • The methods in the DataOutputStream class are capable of writing data of specific types. Java Programming FROM THE BEGINNING 92 Chapter 14: Files Writing Data Types • A partial list of DataOutputStream methods: Description Action void write(int b) Writes the value of b as a single byte. void write(byte[] b, Writes len bytes of the array b, startint off, int len) ing at the position indicated by off. void writeBoolean( Writes the value of v as a byte containboolean v) ing either 1 (true) or 0 (false). void writeByte(int v) Writes the value of v as a single byte. void writeChar(int v) Writes the value of v as 2 bytes. void writeDouble(double v) Writes the value of v as 8 bytes. void writeFloat(float v) Writes the value of v as 4 bytes. void writeInt(int v) Writes the value of v as 4 bytes. void writeLong(long v) Writes the value of v as 8 bytes. void writeShort(int v) Writes the value of v as 2 bytes. Java Programming FROM THE BEGINNING 93 Chapter 14: Files Writing Data Types • All of these methods throw IOException if an error occurs. • An example of using writeInt to write the value of an int variable: out.writeInt(n); Four bytes (the value of n in binary) will be written to the stream. • The other DataOutputStream methods are called in a similar fashion. Java Programming FROM THE BEGINNING 94 Chapter 14: Files Writing Strings • Writing strings to a DataOutputStream is a bit tricky, because a string doesn’t have a fixed size. • DataOutputStream has three different methods for writing strings: writeBytes, writeChars, and writeUTF. • All three methods throw IOException if an error occurs. Java Programming FROM THE BEGINNING 95 Chapter 14: Files Writing Strings • The writeBytes method writes a string as a series of single bytes. • The following call writes three bytes: out.writeBytes("abc"); • The writeChars method writes a string as a series of Unicode characters, each of which requires two bytes. • The following call writes six bytes: out.writeChars("abc"); Java Programming FROM THE BEGINNING 96 Chapter 14: Files Writing Strings • Reading a string that’s been written by either writeBytes or writeChars could be difficult, because there’s no indication in the file itself of how many characters are in the string. • The writeUTF method avoids this problem. • This method uses an encoding scheme known as UTF-8 to write a string in such a way that it can be read later. Java Programming FROM THE BEGINNING 97 Chapter 14: Files Writing Strings • The UTF-8 encoding of a string containing ASCII characters consists of a two-byte integer (the length of the string), followed by the characters in the string, each as a single byte. • Example calls of writeUTF: out.writeUTF("so"); out.writeUTF("far"); These calls write a total of nine bytes: Java Programming FROM THE BEGINNING 98 Chapter 14: Files Writing Strings • In UTF, non-ASCII characters (plus the null character) are stored using two or three bytes. • The two bytes that are written ahead of the string represent the number of bytes in the string’s encoding, not the original length of the string. • The advantage of the writeUTF method is that each string is preceded by its size, making it easy to recover the original string when the file is read. • The readUTF method (in the DataInputStream class) is designed to read strings written in UTF form. Java Programming FROM THE BEGINNING 99 Chapter 14: Files Reading Data Types • In order to read data that was written to a DataOutputStream, a DataInputStream object is needed. • A DataInputStream object is normally layered on top of a FileInputStream object: FileInputStream fileIn = new FileInputStream(fileName); DataInputStream in = new DataInputStream(fileIn); • The parameter for the DataInputStream constructor has type InputStream. Java Programming FROM THE BEGINNING 100 Chapter 14: Files Reading Data Types • A partial list of DataInputStream methods: Description Action int read(byte[] b) Reads bytes into b until the array is full or the end of the file has been reached. Returns the number of bytes read. int read(byte[] b, Reads up to len bytes of data into int off, the array b, starting at the position int len) indicated by off. Returns the number of bytes read. boolean readBoolean() Reads a single byte. Returns false if the byte is 0; returns true otherwise. (continued) Java Programming FROM THE BEGINNING 101 Chapter 14: Files Reading Data Types Description Action byte readByte() Reads a single byte and returns it. char readChar() Reads a 2-byte char value and returns it. double readDouble() Reads an 8-byte double value and returns it. float readFloat() Reads a 4-byte float value and returns it. int readInt() Reads a 4-byte int value and returns it. long readLong() Reads an 8-byte long value and returns it. short readShort() Reads a 2-byte short value and returns it. String readUTF() Reads a string and returns it. The string is assumed to be stored using UTF-8 encoding. • All these methods throw IOException if an error occurs. Java Programming FROM THE BEGINNING 102 Chapter 14: Files Reading Data Types • If one of the read methods fails to read any input because it reaches the end of the file, it returns –1. • Each of the other methods throws EOFException if it fails to read the necessary number of bytes because reaches the end of the file. • An example of using readInt to read an int value: int n = in.readInt(); Java Programming FROM THE BEGINNING 103 Chapter 14: Files 14.6 Reading and Writing Characters • To read and write text files—files that contain character data—the preferred technique is to use reader and writer classes. • Reader objects are capable of translating single bytes into the two-byte Unicode characters that Java uses. • Writer objects perform the opposite translation, converting Unicode characters to single bytes. Java Programming FROM THE BEGINNING 104 Chapter 14: Files Writing to a Text File • The FileWriter class is similar to the FileOutputStream class, except that it’s designed for use with text files. • As it writes characters to a file, a FileWriter object will automatically convert each Unicode character into a single byte. Java Programming FROM THE BEGINNING 105 Chapter 14: Files Writing to a Text File • Constructors for the FileWriter class: Description Action FileWriter( Creates a FileWriter object String fileName) representing the file with the specified name. FileWriter( Creates a FileWriter object String fileName, representing the file with the specified boolean append) name. If append is true, bytes will be written to the end of the file rather than the beginning. FileWriter( Creates a FileWriter object File file) representing the specified File object. • IOException occurs if a file can’t be opened for output. Java Programming FROM THE BEGINNING 106 Chapter 14: Files Writing to a Text File • The FileWriter class has no methods, although it does inherit methods for writing characters from its superclasses. • In most cases, it’s best to create a different kind of object to do the actual writing to a FileWriter object. • The BufferedWriter class is designed for this purpose. • Using BufferedWriter provides greater efficiency than using FileWriter directly. Java Programming FROM THE BEGINNING 107 Chapter 14: Files Writing to a Text File • The single-argument BufferedWriter constructor accepts a FileWriter object (as well as other writer objects) as its argument: FileWriter fileOut = new FileWriter(filename); BufferedWriter bufOut = new BufferedWriter(fileOut); • BufferedWriter provides only a limited set of methods, so it’s usually more convenient to use the methods in the PrintWriter class instead. • PrintWriter provides the familiar print and println methods. Java Programming FROM THE BEGINNING 108 Chapter 14: Files Writing to a Text File • A BufferedWriter object (as well as any writer or output stream object) can be passed to the PrintWriter constructor: PrintWriter out = new PrintWriter(bufOut); • The PrintWriter class supports the print method, which is capable of writing objects as well as values of the primitive types: out.print(3.14); out.print("Testing..."); • When print is used to write an object, the object’s toString method is called. Java Programming FROM THE BEGINNING 109 Chapter 14: Files Writing to a Text File • The PrintWriter class also provides a number of println methods, which are used in the same way as print: out.println(3.14); out.println("Testing..."); Java Programming FROM THE BEGINNING 110 Chapter 14: Files Reading from a Text File • The FileReader class is used for reading characters from a text file. • As it reads from a file, a FileReader object will automatically convert each byte into the corresponding two-byte Unicode character. • The FileReader constructors are analogous to the FileInputStream constructors. • Both constructors throw FileNotFoundException if the file can’t be opened for reading. Java Programming FROM THE BEGINNING 111 Chapter 14: Files Reading from a Text File • The FileReader class inherits methods for reading characters from its superclasses. • In most cases, it’s best to create a BufferedReader object to do the actual reading, however. • Using BufferedReader provides greater efficiency than using FileReader directly. • Also, the BufferedReader class has methods to read whole lines of characters, not just single characters. Java Programming FROM THE BEGINNING 112 Chapter 14: Files Reading from a Text File • The single-argument BufferedReader constructor accepts a FileReader object (as well as other kinds of reader objects) as its argument: FileReader fileIn = new FileReader(filename); BufferedReader in = new BufferedReader(fileIn); Java Programming FROM THE BEGINNING 113 Chapter 14: Files Reading from a Text File • A partial list of BufferedReader methods: Description Action int read() Reads a character, returning it in int form. int read(char[] cbuf, Reads up to len characters of data int off, into the array cbuf, starting at the int len) position indicated by off. Returns the number of characters read. String readLine() Reads a single line of characters and returns it. • All three methods throw IOException if an error occurs. • If one of the read methods fails to read any input because it reaches the end of the file, it returns –1. Java Programming FROM THE BEGINNING 114 Chapter 14: Files Reading from a Text File • The first version of the read method reads one character from a file. • read returns an integer, so a cast is necessary before using the return value as a character. • A loop that reads all the characters in a file: while (true) { int charValue = in.read(); if (charValue == -1) break; // End of file reached char inputChar = (char) charValue; … } Java Programming FROM THE BEGINNING 115 Chapter 14: Files Reading from a Text File • The readLine method reads characters until it encounters a line feed ('\n'), a carriage return ('\r'), or a carriage return followed immediately by a line feed. • readLine returns a string containing all the characters it read, not including the line-feed and/or carriage-return character. • readLine returns null if it was unable to read any characters at all because it reached the end of the input file. Java Programming FROM THE BEGINNING 116 Chapter 14: Files Reading from a Text File • A loop that uses readLine to read all the lines in a file: while (true) { String line = in.readLine(); if (line == null) break; … } Java Programming FROM THE BEGINNING 117 Chapter 14: Files Program: Converting Text to HTML • The ConvertToHTML program converts an ordinary text file into an HTML file. • An HTML (Hypertext Markup Language) file is a text file that contains formatting commands, known as tags. • These commands are used by a browser to format the file for display. Java Programming FROM THE BEGINNING 118 Chapter 14: Files A Sample Text File Java Programming FROM THE BEGINNING 119 Chapter 14: Files Running the ConvertToHTML Program • The ConvertToHTML program expects the name of a text file to be specified on the command line: java ConvertToHTML norton.txt • The program will create a file with the same name, but with the extension .html. Java Programming FROM THE BEGINNING 120 Chapter 14: Files Java Programming FROM THE BEGINNING 121 Chapter 14: Files Viewing norton.html in a Browser Java Programming FROM THE BEGINNING 122 Chapter 14: Files Program Behavior • The ConvertToHTML program will use the first line in the original file as the text for both the <TITLE> and <H2> tags. • Each blank line will be replaced by the line <P> • Nonblank lines after the first one are copied to the HTML file without change. • Certain characters have a special meaning in HTML, including &, <, and >. The program should (but does not) replace these characters by special codes if they appear in the original file. Java Programming FROM THE BEGINNING 123 Chapter 14: Files ConvertToHTML.java // Converts an ordinary text file to an HTML file import java.io.*; public class ConvertToHTML { public static void main(String[] args) { // Terminate program if number of command-line arguments // is wrong if (args.length != 1) { System.out.println("Usage: java ConvertToHTML file"); System.exit(-1); } Java Programming FROM THE BEGINNING 124 Chapter 14: Files // Call the generateHTMLFile method, which converts the // original file to an HTML file. Terminate the program // if an exception occurs. try { generateHTMLFile(args[0]); } catch (IOException e) { System.out.println("Error: " + e.getMessage()); System.exit(-1); } } // Returns a file name that is identical to fileName, but // with the extension changed to .html private static String createHTMLFileName(String fileName) { int index = fileName.lastIndexOf("."); if (index != -1) fileName = fileName.substring(0, index); return fileName + ".html"; } Java Programming FROM THE BEGINNING 125 Chapter 14: Files // Generates an HTML file containing the contents of the // original file, with HTML tags added private static void generateHTMLFile(String originalFile) throws IOException { // Open the original file for reading FileReader fileIn = new FileReader(originalFile); BufferedReader in = new BufferedReader(fileIn); // Determine the name of the HTML file by changing the // extension of the original file name to .html String newFile = createHTMLFileName(originalFile); // Open the HTML file for writing FileWriter fileOut = new FileWriter(newFile); BufferedWriter bufOut = new BufferedWriter(fileOut); PrintWriter out = new PrintWriter(bufOut); Java Programming FROM THE BEGINNING 126 Chapter 14: Files // Read the first line of the original file. Terminate // the program if the original file is empty. String firstLine = in.readLine(); if (firstLine == null) { System.out.println("File is empty"); System.exit(-1); } // Write a series of tags to the HTML file, using the // first line of the original file for the <TITLE> and // <H2> tags. out.println("<HTML>"); out.println("<HEAD>"); out.println("<TITLE>" + firstLine + "</TITLE>"); out.println("</HEAD>"); out.println("<BODY>"); out.println("<H2>" + firstLine + "</H2>"); Java Programming FROM THE BEGINNING 127 Chapter 14: Files // Copy the remaining lines of the original file to the // HTML file, replacing each empty line with a <P> tag while (true) { String currentLine = in.readLine(); if (currentLine == null) break; if (currentLine.length() == 0) out.println("<P>"); else out.println(currentLine); } // Write the closing tags to the HTML file out.println("</BODY>"); out.println("</HTML>"); } } // Close both files in.close(); out.close(); Java Programming FROM THE BEGINNING 128 Chapter 14: Files 14.7 Reading and Writing Objects • Objects can be written to a file and later read back in. • A file that contains objects is always a binary file. • Writing an object to a file involves saving the values of all the object’s instance variables. • In addition, a “tag” will need to be saved to indicate the object’s type. • If any of the values stored in an object are references to other objects, those objects will need to be saved as well. Java Programming FROM THE BEGINNING 129 Chapter 14: Files Serialization • When objects are written to a file, Java uses a process known as serialization. • Java automatically adds information to each object to identify its class. • If the object contains references to other objects, those objects are stored as well. • Each object is stored only once, regardless of the number of references to it. Java Programming FROM THE BEGINNING 130 Chapter 14: Files The Serializable Interface • The Serializable interface is used to indicate that a class is serializable. • Serializable belongs to the java.io package. • A number of classes in the Java API implement the Serializable interface. • If a class is serializable, then all its subclasses are as well. Java Programming FROM THE BEGINNING 131 Chapter 14: Files The Serializable Interface • To make a class serializable, the words implements Serializable are added to its declaration: class MyClass implements Serializable { … } • Serializable is empty, so MyClass doesn’t need to contain any particular methods. Java Programming FROM THE BEGINNING 132 Chapter 14: Files The Serializable Interface • Two other conditions are required in order for a class to be serializable: – If any of the instance variables in the class contain objects, these objects must belong to serializable classes. – The class’s direct superclass must have a no-arg constructor or be serializable. • If a program tries to write an object that’s not serializable, a NotSerializableException will occur. Java Programming FROM THE BEGINNING 133 Chapter 14: Files Writing Objects • In order to write objects to a file, an instance of the ObjectOutputStream class is needed. • This instance will be layered on top of an instance of the FileOutputStream class: FileOutputStream fileOut = new FileOutputStream(fileName); ObjectOutputStream out = new ObjectOutputStream(fileOut); • The argument to the ObjectOutputStream constructor can be any kind of output stream. • The constructor may throw IOException. Java Programming FROM THE BEGINNING 134 Chapter 14: Files Writing Objects • The most important ObjectOutputStream method is writeObject, which is used to write an object to a stream. • The parameter to writeObject has type Object, so any object can be passed to writeObject. • The String class implements the Serializable interface, so a string can be passed to writeObject: out.writeObject("Testing..."); Java Programming FROM THE BEGINNING 135 Chapter 14: Files Writing Objects • Exceptions that can arise as a result of calling writeObject: – IOException – InvalidClassException – NotSerializableException • InvalidClassException and NotSerializableException are indirect subclasses of IOException, so a catch block for IOException will handle those as well. Java Programming FROM THE BEGINNING 136 Chapter 14: Files Writing Objects • The remaining ObjectOutputStream methods closely resemble DataOutputStream methods. • These methods are used to write other types of data, such as integers, floating-point numbers, and characters. • By providing these methods, the ObjectOutputStream class allows a file to contain a mixture of objects and values of the primitive types. Java Programming FROM THE BEGINNING 137 Chapter 14: Files Reading Objects • Before objects can be read, an instance of the ObjectInputStream class must be created: FileInputStream fileIn = new FileInputStream(fileName); ObjectInputStream in = new ObjectInputStream(fileIn); • The argument to the ObjectInputStream constructor can be any kind of input stream. Java Programming FROM THE BEGINNING 138 Chapter 14: Files Reading Objects • The ObjectInputStream constructor may throw IOException and StreamCorruptedException (the format of the underlying file is wrong). • A catch block for IOException can handle both exceptions. • Objects are read from an ObjectInputStream by calling the readObject method. • readObject returns an Object value, which can then be cast to the proper type: String str = (String) in.readObject(); Java Programming FROM THE BEGINNING 139 Chapter 14: Files Reading Objects • Exceptions that can arise as a result of calling readObject: – ClassNotFoundException—The Java interpreter is unable to locate the class to which the object belongs. – InvalidClassException—There’s a problem with the class to which the object belongs. – IOException—The underlying stream has a problem. – OptionalDataException—The readObject method encountered unexpected data. – StreamCorruptedException—The readObject method encountered an inconsistency in the data. Java Programming FROM THE BEGINNING 140 Chapter 14: Files Reading Objects • A catch block for IOException will also handle InvalidClassException, OptionalDataException, and StreamCorruptedException. • ClassNotFoundException is not a subclass of IOException, however, so it will need a separate catch block. Java Programming FROM THE BEGINNING 141 Chapter 14: Files Reading Objects • The remaining ObjectInputStream methods are used to read other types of data, such as integers, floating-point numbers, and characters. • These methods closely resemble DataInputStream methods. Java Programming FROM THE BEGINNING 142 Chapter 14: Files Reading and Writing Entire Data Structures • Data structures are objects, so they can be written to a file with a single method call. • An example of writing an array to a file: int[] a = {1, 2, 3}; try { FileOutputStream fileOut = new FileOutputStream(fileName); ObjectOutputStream out = new ObjectOutputStream(fileOut); out.writeObject(a); out.close(); } catch (IOException e) { System.out.println(e.getMessage()); } Java Programming FROM THE BEGINNING 143 Chapter 14: Files Reading and Writing Entire Data Structures • Code that reads the array back in: int[] a; try { FileInputStream fileIn = new FileInputStream(fileName); ObjectInputStream in = new ObjectInputStream(fileIn); a = (int[]) in.readObject(); in.close(); } catch (IOException e) { System.out.println(e.getMessage()); } catch (ClassNotFoundException e) { System.out.println(e.getMessage()); } Java Programming FROM THE BEGINNING 144 Chapter 14: Files Reading and Writing Entire Data Structures • Similar code can be used to write a vector or other data structure and then read it later. • When serializing a data structure, make sure that the elements of the data structure are serializable. • Otherwise, a NotSerializableException will occur when the program is executed. Java Programming FROM THE BEGINNING 145 Chapter 14: Files 14.8 Case Study: A Phone Directory (Revisited) • The original PhoneDirectory program (Section 5.8) allowed the user to enter names and numbers as well as look up existing names. • Unfortunately, the names and phone numbers were lost when the program terminates. • The new version of PhoneDirectory will save the names and numbers in a file when it terminates. • When the program is run the next time, it restores the names and numbers by reading from the file. Java Programming FROM THE BEGINNING 146 Chapter 14: Files Changes to the PhoneDirectory Program • Other differences between the old and new versions of PhoneDirectory: – PhoneRecord objects will be stored in a vector rather than an array. – The new program relies on helper methods instead of a single main method. • Helper methods: – – – – addNumber—Implements the a (add) command findNumber—Implements the f (find) command saveRecords—Saves records in a file readRecords—Reads records from the same file Java Programming FROM THE BEGINNING 147 Chapter 14: Files Changes to the PhoneDirectory Program • saveRecords will save the vector of records in a file named records.dat. • If readRecords is unable to read from the records.dat file (or the file doesn’t exist), the method will display a message and create an empty vector. • The PhoneRecord class is the same as before, except that implements Serializable will be added to the class declaration. Java Programming FROM THE BEGINNING 148 Chapter 14: Files PhoneDirectory2.java // // // // // // // // // // // // // // // Program name: PhoneDirectory2 Author: K. N. King Written: 1999-12-22 Stores names and telephone numbers and allows phone numbers to be looked up. The user is given a menu of three commands: a - Add a new phone number f - Find a phone number q - Quit The "a" command prompts the user to enter a name and a number, which are then stored in the program's database. Java Programming FROM THE BEGINNING 149 Chapter 14: Files // // // // // // // // // // // // The "f" command prompts the user to enter a name; the program then displays all matching names in the database, along with the corresponding phone numbers. It is not necessary to enter the entire name; all names that begin with the specified characters will be displayed. The "f" command ignores the case of letters when looking for matching names. The and the and "q" command causes the program to terminate. The names numbers are saved in a file named "records.dat". When program is run the next time, it will open the file read the records. import java.io.*; import java.util.*; import jpb.*; Java Programming FROM THE BEGINNING 150 Chapter 14: Files public class PhoneDirectory2 { // Class variables private static final String DATA_FILE = "records.dat"; private static Vector records; public static void main(String[] args) { // Read records from data file readRecords(); // Display list of commands System.out.println("Phone directory commands:\n" + " a - Add a new phone number\n" + " f - Find a phone number\n" + " q - Quit\n"); Java Programming FROM THE BEGINNING 151 Chapter 14: Files // Read and execute commands while (true) { // Prompt user to enter a command SimpleIO.prompt("Enter command (a, f, or q): "); String command = SimpleIO.readLine().trim(); // Determine whether command is "a", "f", "q", or // illegal; execute command if legal. if (command.equalsIgnoreCase("a")) { // Command is "a". Call addNumber to add a new // name and number to the database addNumber(); } else if (command.equalsIgnoreCase("f")) { // Command is "f". Call findNumber to find phone // numbers that match the user's criteria. findNumber(); Java Programming FROM THE BEGINNING 152 Chapter 14: Files } else if (command.equalsIgnoreCase("q")) { // Command is "q". Save records in data file and // terminate program. saveRecords(); return; } else { // Command is illegal. Display error message. System.out.println("Command was not recognized; " + "please enter only a, f, or q."); } System.out.println(); } } Java Programming FROM THE BEGINNING 153 Chapter 14: Files /////////////////////////////////////////////////////////// // NAME: addNumber // BEHAVIOR: Prompts the user for a name and number, // then creates a phone record and stores it in // the records vector. // PARAMETERS: None // RETURNS: Nothing /////////////////////////////////////////////////////////// private static void addNumber() { SimpleIO.prompt("Enter new name: "); String name = SimpleIO.readLine().trim(); SimpleIO.prompt("Enter new phone number: "); String number = SimpleIO.readLine().trim(); records.addElement(new PhoneRecord(name, number)); } Java Programming FROM THE BEGINNING 154 Chapter 14: Files /////////////////////////////////////////////////////////// // NAME: findNumber // BEHAVIOR: Prompts the user for a search key. Searches // the records vector for records whose names // begin with the search key. Prints these // names and the corresponding phone numbers. // PARAMETERS: None // RETURNS: Nothing /////////////////////////////////////////////////////////// private static void findNumber() { SimpleIO.prompt("Enter name to look up: "); String key = SimpleIO.readLine().trim().toLowerCase(); for (int i = 0; i < records.size(); i++) { PhoneRecord currentRecord = (PhoneRecord) records.elementAt(i); String name = currentRecord.getName().toLowerCase(); if (name.startsWith(key)) System.out.println(currentRecord.getName() + " " + currentRecord.getNumber()); } } Java Programming FROM THE BEGINNING 155 Chapter 14: Files /////////////////////////////////////////////////////////// // NAME: readRecords // BEHAVIOR: Restores the records vector to its previous // state by reading it (as a single object) // from the data file. Creates an empty vector // if the file does not exist or cannot be // read. // PARAMETERS: None // RETURNS: Nothing /////////////////////////////////////////////////////////// private static void readRecords() { try { FileInputStream fileIn = new FileInputStream(DATA_FILE); ObjectInputStream in = new ObjectInputStream(fileIn); records = (Vector) in.readObject(); in.close(); } catch (Exception e) { System.out.println(DATA_FILE + " does not exist or " + "cannot be read\n"); records = new Vector(); } } Java Programming FROM THE BEGINNING 156 Chapter 14: Files /////////////////////////////////////////////////////////// // NAME: saveRecords // BEHAVIOR: Saves the records vector (as a single // object) by writing it to the data file. // PARAMETERS: None // RETURNS: Nothing /////////////////////////////////////////////////////////// private static void saveRecords() { try { FileOutputStream fileOut = new FileOutputStream(DATA_FILE); ObjectOutputStream out = new ObjectOutputStream(fileOut); out.writeObject(records); out.close(); } catch (IOException e) { System.out.println("Error writing to " + DATA_FILE); } } } Java Programming FROM THE BEGINNING 157 Chapter 14: Files // Represents a record containing a name and a phone number class PhoneRecord implements Serializable { private String name; private String number; /////////////////////////////////////////////////////////// // NAME: PhoneRecord // BEHAVIOR: Constructs a phone record containing the // specified name and phone number // PARAMETERS: personName - name of a person // phoneNumber - phone number for that person /////////////////////////////////////////////////////////// public PhoneRecord(String personName, String phoneNumber) { name = personName; number = phoneNumber; } Java Programming FROM THE BEGINNING 158 Chapter 14: Files /////////////////////////////////////////////////////////// // NAME: getName // BEHAVIOR: Returns the name stored in this record // PARAMETERS: None // RETURNS: The name stored in this record /////////////////////////////////////////////////////////// public String getName() { return name; } /////////////////////////////////////////////////////////// // NAME: getNumber // BEHAVIOR: Returns the phone number stored in this // record // PARAMETERS: None // RETURNS: The phone number stored in this record /////////////////////////////////////////////////////////// public String getNumber() { return number; } } Java Programming FROM THE BEGINNING 159