Lesson 7 | File Streams |
Objective | Learn how to work with file streams in Java, including basic file I/O and object serialization. |
Java File Streams and Object Serialization
Up to this point, most examples have used System.out
and System.in
for input and output. In real applications, however, streams are more often attached to files or network connections. The FileInputStream
and FileOutputStream
classes are concrete subclasses of InputStream
and OutputStream
, providing byte-oriented access to files.
FileInputStream and FileOutputStream
These classes inherit common stream methods such as:
read()
write()
available()
flush()
To create them, you typically use constructors that accept a filename or
Path
object:
public FileInputStream(String filename) throws IOException
public FileOutputStream(String filename) throws IOException
Copying Files with Try-with-Resources
The example below demonstrates a copyFile
method that copies data from one file to another. It uses a buffer for efficiency and leverages the try-with-resources construct to ensure streams are closed automatically:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Path;
public class FileCopy {
public static void main(String[] args) {
Path sourcePath = Path.of("source.txt");
Path destinationPath = Path.of("destination.txt");
try {
copyFile(sourcePath, destinationPath);
System.out.println("File copied successfully.");
} catch (IOException e) {
System.err.println("Failed to copy the file: " + e.getMessage());
}
}
public static void copyFile(Path source, Path destination) throws IOException {
try (FileInputStream fis = new FileInputStream(source.toFile());
FileOutputStream fos = new FileOutputStream(destination.toFile())) {
byte[] buffer = new byte[8192]; // 8 KB buffer
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
}
}
}
This implementation works for both text and binary files. The 8 KB buffer improves performance by reducing I/O operations.
👉 For simple file copies, Java’s Files.copy()
(from java.nio.file
) is an even shorter alternative.
Object Serialization with ObjectOutputStream
The ObjectOutputStream
and ObjectInputStream
classes are higher-level streams that allow objects to be serialized (converted into a byte stream) and deserialized (reconstructed). These are typically wrapped around FileOutputStream
and FileInputStream
.
import java.io.*;
class Cat implements Serializable { }
public class SerializeCat {
public static void main(String[] args) {
Cat c = new Cat();
try (FileOutputStream fs = new FileOutputStream("cat.ser");
ObjectOutputStream os = new ObjectOutputStream(fs)) {
os.writeObject(c); // Serialize
} catch (Exception e) {
e.printStackTrace();
}
try (FileInputStream fis = new FileInputStream("cat.ser");
ObjectInputStream ois = new ObjectInputStream(fis)) {
c = (Cat) ois.readObject(); // Deserialize
} catch (Exception e) {
e.printStackTrace();
}
}
}
- Serializable marker: The
Cat
class implements Serializable
, which enables its instances to be serialized.
- Serialization: A
FileOutputStream
is wrapped in an ObjectOutputStream
, and writeObject()
is called to serialize the object.
- Deserialization: The object is restored by calling
readObject()
from an ObjectInputStream
and casting back to Cat
.
⚠️ Note: Java serialization is still supported, but modern applications often prefer formats like JSON or XML for safer and more portable object persistence.
