Writing Files
The java.io.FileOutputStream class is a concrete subclass of java.io.OutputStream that provides output streams connected to files. This class has all the usual methods of output streams, such as write( ), flush( ), and close( ), which are used exactly as they are for any other output stream.
FileOutputStream( ) has three main constructors, differing primarily in how the file is specified:
public FileOutputStream(String filename) throws IOException public FileOutputStream(File file) throws IOException public FileOutputStream(FileDescriptor fd)
The first constructor uses a string containing the name of the file; the second constructor uses a java.io.File object; the third constructor uses a java.io.FileDescriptor object. To write data to a file, just pass the name of the file to the FileOutputStream( ) constructor, then use the write( ) methods as usual. If the file does not exist, all three constructors will create it. If the file does exist, any data inside it will be overwritten.
A fourth constructor also lets you specify whether the file's contents should be erased before data is written into it (append == false) or whether data is to be tacked onto the end of the file (append == true):
public FileOutputStream(String name, boolean append) throws IOException
Output streams created by the other three constructors simply overwrite the file; they do not provide an option to append data to the file.
Java looks for files in the current working directory. You can write to a file in a different directory by passing a full or relative path to the file from the current working directory. For example, to append data to the Windowsjavajavalog.txt file no matter which directory is current, you would do this:
FileOutputStream fout = new FileOutputStream("/Windows/java/javalog.txt", true);
Although Windows uses a backslash as the directory separator, Java still expects you to use a forward slash as in Unix. Hardcoded pathnames are dangerously platform-dependent. Using this constructor automatically classifies your program as impure Java. As with input streams, a slightly less dangerous alternative builds a File object a piece at a time like so:
File[] roots = File.listRoots( ) File windows = new File(roots[0], "Windows"); File java = new File(windows, "java"); File javalog = new File(java, "javalog.txt"); FileInputStream fis = new FileInputStream(javalog);
Untrusted code is normally not allowed to write files either. If an applet tries to create a FileOutputStream, the constructor throws a SecurityException.
The FileOutputStream class has one method that's not declared in java.io.OutputStream: getFD( ).
public final FileDescriptor getFD( ) throws IOException
This method returns the java.io.FileDescriptor object associated with this stream.
Example 4-2 reads two filenames from the command line, then copies the first file into the second file. The StreamCopier class from Example 3-3 in the previous chapter does the actual reading and writing.
Example 4-2. The FileDumper program
import java.io.*; import com.elharo.io.*; public class FileCopier { public static void main(String[] args) { if (args.length != 2) { System.err.println("Usage: java FileCopier infile outfile"); } try { copy(args[0], args[1]); } catch (IOException ex) { System.err.println(ex); } } public static void copy(String inFile, String outFile) throws IOException { FileInputStream fin = null; FileOutputStream fout = null; try { fin = new FileInputStream(inFile); fout = new FileOutputStream(outFile); StreamCopier.copy(fin, fout); } finally { try { if (fin != null) fin.close( ); } catch (IOException ex) { } try { if (fout != null) fout.close( ); } catch (IOException ex) { } } } } |
Since we're no longer writing to System.out and reading from System.in, it's important to make sure the streams are closed when we're done. This is a good use for a finally clause, as we need to make sure the files are closed whether the reads and writes succeed or not.
Java is better about closing files than most languages. As long as the VM doesn't terminate abnormally, the files will be closed when the program exits. Still, if this class is used inside a long-running program like a web server, waiting until the program exits isn't a good idea; other threads and processes may need access to the files.
Example 4-2 has one bug: the program does not behave well if the input and output files are the same. While it would be straightforward to compare the two filenames before copying, this is not safe enough. Once aliases, shortcuts, symbolic links, and other factors are taken into account, a single file may have multiple names. The full solution to this problem will have to wait until Chapter 17, where I discuss canonical paths and temporary files.