The
java.io.FileOutputStream
class is
a concrete subclass of java.io.OutputStream
that
provides output streams connected to files.
public class FileOutputStream extends OutputStream
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.
public native void write(int b) throws IOException public void write(byte[] data) throws IOException public void write(byte[] data, int offset, int length) throws IOException public native void close() throws IOException
These are all implemented in native code except for the two multibyte
write()
methods. These, however, just pass their
arguments on to a private native method called
writeBytes()
, so effectively all these methods are
implemented with native code.
There are three main
FileOutputStream()
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. I will avoid using
the second and third constructors until I’ve discussed
File
objects and file descriptors (Chapter 12). To write data to a file, just pass the name
of the file to the FileOutputStream()
constructor,
then use the write()
methods as normal. 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
). The other three constructors simply
overwrite the file; they do not provide an option to append data to
the file.
public FileOutputStream(String name, boolean append) throws IOException
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 \Windows\java\javalog.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, at least in Java 1.1. Hardcoded pathnames are dangerously platform-dependent. Using this constructor automatically classifies your program as impure Java. We will take this up in more detail in Chapter 12.
Untrusted applets are normally not allowed to read or write files. 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.
The FileOutputStream
class also has a protected
finalize()
method that’s invoked before a
FileOutputStream
object is garbage-collected. This
method ensures that files are properly flushed and closed before the
file output stream that opened them is garbage-collected. You
normally don’t need to invoke this method explicitly. If you
subclass FileOutputStream
and override
finalize()
, the subclass’s
finalize()
method should invoke this
finalize()
method by calling
super.finalize()
.
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 last chapter is used to do the actual
reading and writing.
Example 4-2. The FileCopier Program
import java.io.*; import com.macfaq.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 e) {System.err.println(e);} } 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 e) { } try { if (fout != null) fout.close(); } catch (IOException e) { } } } }
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 or not the reads and writes
succeed.
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.
There is one bug in this program: it 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 12, when we discuss canonical paths and temporary files.
Get Java I/O now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.