The File class (Section 14.2)
File query methods
File manipulation methods
The FileOutputStream class (Section 14.3)
Constructors
Writing methods
The FileInputStream class (Section 14.3)
Constructors
Reading methods
Case study: Copying a file
Textbook: Section 14.2
Java has a large library of classes in the java.io package related to working with files. It's quite extensive and confusing. Today we're going to investigate three of the basic classes: File, FileOutputStream, and FileInputStream.
A File object represents the name of a file on the disk. It allows you to work with the file as an entire object - not looking inside the file, but asking questions about the file's attributes or doing something that works with the file as a whole.
The most basic constructor for a File takes a string as parameter giving the complete location of where the file is located.
This creates a File variable source, representing the file name ``CopyFile.java''. The parameter names the file just as you would name regular files. In Unix, a path name beginning with slash (`/') is relative to the root directory, and otherwise it is relative to the current directory (from which you invoked the program). You can name directories to move down the file hierarchy (or use the ``..'' directory to move up the hierarchy).File source = new File("CopyFile.java");
File source2 = new File("CS160/Lab9/CopyFile.java"); File source3 = new File("/usr/people/classes/Java/csbsju");
The File object represents a file's name, not the file itself. Thus, you can create a File object to represent a name, even if a file of that name doesn't exist.
The first set of methods that you can perform on a File object relate to answering questions about the file.
For example:
if(!source.exists()) { IO.println("The file ``" + source.getName() + "'' doesn't exist."); System.exit(-1); } else if(!source.canRead()) { IO.println("The file ``" + source.getName() + "'' exists, " + "but it isn't accessible."); System.exit(-1); }
There are also some for manipulating the file whole.
Textbook: Section 14.3
A FileOutputStream object represents a file into which you can write bytes. It's for writing raw bytes - we'll get into more sophisticated objects for writing characters or writing more complex items later.
When you create a FileOutputStream object, the file is opened for writing. The file is set up so that the first write occurs at the first byte of the file - thus, effectively, when you create a FileOutputStream object, the file it represents is immediately cleared.
A FileNotFoundException is an inappropriate name for what this method does, since if the file doesn't exist, it goes ahead and creates one. This exception is raised when creating the file is impossible - for example, if the file exists, but you don't have write permission for it. Or if it doesn't exist, and you don't have permission to create a new file in the directory.
As you write into the file, the FileOutputStream object tracks where you currently are in the file. This is called the file pointer.
Additionally, operating systems typically restrict each program on how many files they can have open at once. So if you don't close it, but your program tries to open several files later on, you may find that on the 64th file the program is suddenly no longer saving.
Textbook: Section 14.3
The FileInputStream class is basically the opposite of the FileOutputStream class - a FileInputStream object is for reading bytes from a file.
Let's look at a program that illustrates these three classes at work. This program copies one file into another location. You would invoke it via the command line; for example, if you wanted to copy the file ``CopyFile.java'' into a file named ``CopyFile.bak'', you would type the following at the Unix prompt.
% java CopyFile CopyFile.java CopyFile.bak
1 import java.io.*; 2 public class CopyFile { 3 public static void main(String[] args) { 4 if(args.length != 2) { 5 System.out.println("usage: java CopyFile source dest"); 6 return; 7 } 8 File src_f = new File(args[0]); 9 File dst_f = new File(args[1]); 10 if(!src_f.exists() || !src_f.canRead()) { 11 System.out.println("cannot find source: " + src_f.getName()); 12 return; 13 } else if(dst_f.exists()) { 14 System.out.println("destination file " + dst_f.getName() 15 + " already exists"); 16 return; 17 } 18 try { 19 FileInputStream src = new FileInputStream(src_f); 20 FileOutputStream dst = new FileOutputStream(dst_f); 21 byte[] buffer = new byte[512]; 22 while(true) { 23 int count = src.read(buffer); 24 if(count == -1) break; 25 dst.write(buffer, 0, count); 26 } 27 src.close(); 28 dst.close(); 29 } catch(IOException e) { 30 System.out.println("Error during copy: " + e.getMessage()); 31 if(dst_f.exists()) dst_f.delete(); 32 } 33 } 34 }
One non-obvious thing about this program is the try block: We know the FileInputStream and FileOutputStream constructor methods throw a FileNotFoundException, and yet the only exception we catch is an IOException object. The reason we could get away with this is that FileNotFoundException is a subclass of IOException, so that, if the constructor invoked in line 19 throws a FileNotFoundException, the catch clause of line 29 would still catch it, since the exception is also an IOException.