File Input/Output
The File class from java.io
package is used to represent a file in Java. In Java 7, the java.nio
package was introduced. This package contains a number of useful file-related classes. When possible, you should use favor the Path
interface instead of the File
class (see below).
File Management with java.nio.file
Package
The Path
Interface
Objects implementing the Path
interface are used to locate a file/directory in a file system.
Path
objects can be created by:
Path textFile = Paths.get("stuff.txt");
Starting in Java 11, Path
objects are created by calling a class method:
Path textFile = Path.of("stuff.txt");
This associates the textFile
object with a file or directory called stuff.txt
located in the IntelliJ project directory. The following methods are likely self-explanatory:
// Command Result
textFile.toString(); // "stuff.txt"
textFile.toAbsolutePath().toString(); // "C:\w\stuff.txt"
textFile.toFile(); // a File object representing the Path
Files Class
The Files
class provides a number of class methods to facilitate working with files.
Here are a few methods that will likely prove useful to you:
Files.exists(Path)
— Returns true if the path exists on the file systemFiles.isDirectory(Path)
— Returns true if the directory existsFiles.isRegularFile(Path)
— Returns true if this is a regular fileFiles.isReadable(Path)
— Returns true if it is possible to read from the fileFiles.isWritable(Path)
— Returns true if it is possible to write to the fileFiles.size(Path)
— Returns the size of the file in bytesFiles.readString(Path)
— Reads all content from the file and returns it as oneString
(only available starting in Java 11)Files.readAllLines(Path)
— Reads all content from the file returns aList<String>
of linesFiles.writeString(Path,CharSequence)
— Writes aCharSequence
(thinkString
) to a file (only available starting in Java 11)Files.newInputStream(Path)
— Returns anInputStream
attached to the fileFiles.newOutputStream(Path)
— Returns anOutputStream
attached to the file
Streams of Data
- File input and output can be managed through data streams.
- These file streams function in much the same way as the console input and output streams do.
- There are a variety of standard streams provided in Java
- Low-level file I/O for raw byte I/O, similar to System.in.read() and System.out.write()
- InputStream — Abstract class whose subclasses read raw bytes
- OutputStream — Abstract class whose subclasses write raw bytes
- FileInputStream — reads raw bytes
- FileOutputStream — writes raw bytes
- High-level file I/O for I/O of primitive data types
- DataInputStream — reads primitive data types
- DataOutputStream — writes primitive data types
- Text file I/O for I/O of text
- Scanner — reads and parses text
- PrintWriter — writes text
- Object file I/O for I/O of arbitrary object data
- ObjectInputStream — reads arbitrary object data
- ObjectOutputStream — writes arbitrary object data
Low-Level File I/O
- Low-Level refers to techniques being closer to what the computer understands whereas High-Level refers to techniques being abstracted away from how the computer understands them.
- Typically High-Level is more intuitive for humans but usually requires more steps (for the computer anyway) to accomplish.
- Low-Level file I/O involves reading and writing byte chunks of data.
- Writing involves use of the FileOutputStream.write() method which can accept either a byte value (specified as an int) or an array of bytes.
- The following code appends some data to a file. The file is created if it did not exist prior to running this code:
public static void writeBytes(Path path, boolean append) {
OpenOption openOption = append ? StandardOpenOption.APPEND : StandardOpenOption.CREATE;
try (OutputStream out = Files.newOutputStream(path, openOption)) {
out.write('a');
out.write('d');
out.write(1000000);
out.write(-1000000);
byte[] byteArray = { 5, 20, 32, 'C', 'T' };
out.write(byteArray);
System.out.println("Data written");
}
catch(FileNotFoundException e) {
System.err.println("File not found");
System.err.println(e.getMessage());
}
catch(IOException e) {
System.err.println(e.getMessage());
}
}
- Reading involves use of a FileInputStream.read() method. Two of these are:
- FileInputStream.read() which returns the byte (as an int) that it read.
- FileInputStream.read(byte[] b) which reads up to b.length bytes from the input stream and places them in the byte array. The method returns the number of bytes that it read from the input stream.
- The following code reads some data from a file:
public static void readBytes(Path path) {
try (InputStream in = Files.newInputStream(path)) {
int i = in.read();
int j = in.read();
int k = in.read();
int l = in.read();
int m = in.read();
byte[] byteArray = new byte[5];
int count = in.read(byteArray);
if(5!=count) {
System.err.println("Couldn't find five bytes in the file. Only read " + count);
}
System.out.println("Data read");
System.out.println("" + i + " " + j + " " + k + " " + l + " " + m);
}
catch(FileNotFoundException e) {
System.err.println("File not found");
System.err.println(e.getMessage());
}
catch(IOException e) {
System.err.println(e.getMessage());
}
}
High-Level File I/O
-
High-level file I/O involves making use of an additional layer of classes to add "improved" functionality.
-
The DataInputStream makes use of a FileInputStream object to do the actual work.
-
The DataInputStream class provides methods to read primitive data types:
- readBoolean()
- readByte()
- readChar()
- readInt()
- readDouble()
- readFloat()
- read(byte[] b) — same as FileInputStream's method
-
The DataOutputStream makes use of a FileOutputStream object to do the actual work.
-
The DataOutputStream class provides methods to write primitive data types:
- writeBoolean()
- writeByte()
- writeChar()
- writeInt() — writes four bytes, high byte first
- writeLong() — writes eight bytes, high byte first
- writeDouble() — converts to long using Double.doubleToLongBits then uses writeLong()
- writeFloat() — converts to long using Float.floatToIntBits then uses writeInt()
- writeChars() — Writes a String
-
What follows is an example.
public static void writePrimitives(Path path, boolean append) {
OpenOption openOption = append ? StandardOpenOption.APPEND : StandardOpenOption.CREATE;
try (OutputStream out = Files.newOutputStream(path, openOption);
DataOutputStream dOut = new DataOutputStream(out)) {
dOut.writeInt(43);
dOut.writeBoolean(false);
dOut.writeDouble(Math.PI);
System.out.println("Data written");
}
catch(FileNotFoundException e) {
System.err.println("File not found");
System.err.println(e.getMessage());
}
catch(IOException e) {
System.err.println(e.getMessage());
}
}
public static void readPrimitives(Path path) {
try (InputStream in = Files.newInputStream(path);
DataInputStream dIn = new DataInputStream(in)) {
int i = dIn.readInt();
boolean j = dIn.readBoolean();
double k = dIn.readDouble();
System.out.println("Data read");
System.out.println("" + i + " " + j + " " + k);
}
catch(FileNotFoundException e) {
System.err.println("File not found");
System.err.println(e.getMessage());
}
catch(IOException e) {
System.err.println(e.getMessage());
}
}
Text File I/O
- Often times we may prefer to store data in a file as text instead of raw binary data.
- Doing so makes it possible for someone to read and interpret the data file created by our Java program.
- In much the same way as we display text to the console (using System.out.print() or System.out.println()), we can send text to a file.
- A PrintWriter object has all of the same functionality as System.out.
- In fact, System.out is an object from the PrintStream class which is very similar to the PrintWriter class... Yes, that's right, System.out is an object (and so is System.in).
- The following code creates an input file (if a directory with the same name doesn't already exist) and writes one line of text to it:
public static void writeText(String filename) {
try (PrintWriter out = new PrintWriter(filename)) {
out.println("This is so much fun!");
System.out.println("Data written");
}
catch(FileNotFoundException e) {
System.err.println("File not found");
System.err.println(e.getMessage());
}
}
- System.in is an InputStream object.
- The easiest way to read information from the keyboard is to attach a Scanner object to the System.in input stream.
- In much the same way, we can read text data from a file by attaching a Scanner object to a FileInputStream.
- The following code creates a file, writes some text to it, and then reads and displays each word on a separate line.
public static void readText(Path path) {
try (Scanner in = new Scanner(path)) {
System.out.println("Reading data:");
while(in.hasNextLine()) {
System.out.println(in.nextLine());
}
}
catch(FileNotFoundException e) {
System.err.println("File not found");
System.err.println(e.getMessage());
}
catch(IOException e) {
System.err.println(e.getMessage());
}
}
Object File I/O
- If we want to store the value of an object in a file, one way would be to store the value of each field.
- This may require a significant amount of code to accomplish.
- However, each object in our Java program is stored in memory as a bunch of ones and zeros.
- What if we could just write this "memory footprint" to a file automatically.
- That's the idea behind Object File I/O.
- ObjectOutputStream provides a writeObject() method
- The data is stored in binary form.
- The code following creates a file and saves three objects (an
Integer
,String
andDate
) in it:
try (OutputStream os = Files.newOutputStream(Path.of("test3.dat"));
ObjectOutputStream out = new ObjectOutputStream(os)) {
out.writeInt(178);
out.writeObject("I am a string");
out.writeObject(new Date());
}
- ObjectInputStream provides a readObject() method
- The following code reads the three values back into objects:
try (InputStream is = Files.newInputStream(Path.of("test3.dat"));
ObjectInputStream in = new ObjectInputStream(is)) {
int i = in.readInt();
String phrase = (String)in.readObject();
Date date = (Date)in.readObject();
}
- Notice that the value returned from the readObject() call must be cast to the appropriate type of object.
- Any kind of object can be saved in this way as long as the class declaration includes "implements Serializable" e.g.,
class SomeSillyClass implements Serializable {
// ...
}
- The class doesn't have to actually implement any additional methods (except under special situations when a class may be required to have a custom implementation of readObject and writeObject)