Java Streams   «Prev  Next»

Lesson 7File Streams
ObjectiveLearn 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:
  1. read()
  2. write()
  3. available()
  4. 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();
        }
    }
}
  1. Serializable marker: The Cat class implements Serializable, which enables its instances to be serialized.
  2. Serialization: A FileOutputStream is wrapped in an ObjectOutputStream, and writeObject() is called to serialize the object.
  3. 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.

SEMrush Software