ECS2301 Software Engineering and Project – Lesson 12 (file handling)

File handling with Java

Streams are a way to efficiently process and manipulate data in Java. When it comes to File I/O (Input/Output) operations, Java provides a set of classes for working with files, and streams are commonly used to read from or write to files.

The source of stream can come from a file, console (eg: keyboard) or a network connection (via a socket). The destination also can be a file, console (eg: display monitor or printer) or a network connection.

Let’s break down the basics:

File I/O Classes:

  • File: Represents a file or directory path.
  • FileInputStream and FileOutputStream: Used for reading and writing binary data from/to files.
  • FileReader and FileWriter: Used for reading and writing character data from/to files.

Java Streams:

  • In Java, streams are sequences of data elements that support various operations to perform computations on those elements.
  • When dealing with file I/O, streams make it easy to read or write data in a sequential manner.

Java 8 Stream API:

  • With Java 8, the Stream API was introduced, providing a powerful way to process collections of data.
  • For File I/O, you can use Files.lines() to get a stream of lines from a file.

The java.nio package provides a more flexible and scalable I/O framework compared to the older java.io package. It is particularly well-suited for scenarios with high-performance requirements, such as large-scale data processing, networking, and file manipulation.

Simple text file reader

Here’s very simple code to test text file writing. The following code will create a new file inside your project folder. Notice the import of java.io package, also the main() function has throws IOException, but we do not have try-catch block. This is not a good practice.

import java.io.FileWriter;
import java.io.IOException;

/**
 * Simple character file writer
 *
 * @author azmeer
 */
public class W14aFiles1 {

    /**
     * This throws IOException, but we are not catching it !
     * @param args the command line arguments
     */
    public static void main(String[] args) throws IOException {
        System.out.println("Creating a character file with FileWriter.");
        FileWriter out = new FileWriter("output.txt"); //create a new file
        out.write("Sample text from FileWriter tester."); //write to file
        out.close(); //close the file
    }
}

Text file reader/write with exception handling

The next example is a complete one with try-catch blocks. It shows how to read and write a text file. When you run it for the first time, you will get an error message “java.io.FileNotFoundException: input.txt (The system cannot find the file specified)”. Please create a new file “input.txt”, type some text and run again. Then you will notice a new file “output.txt” having the same content as the “input.txt” file.

import java.io.*;

/**
 * Complete character file writer with exception handling
 *
 * @author azmeeer
 */
public class W14bTextfileReadwriter {

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) throws IOException {
        FileInputStream in = null;
        FileOutputStream out = null;
        try {
            in = new FileInputStream("input.txt");
            out = new FileOutputStream("output.txt");

            int c;
            while ((c = in.read()) != -1) {
                out.write(c);
            }
        } catch (IOException i) {
            System.out.println(i);
        } finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
    }
}

Binary file reader/write with exception handling

Next example read a binary file (ie: input.jpg) and writes it back to the disk.

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * Complete binary file read/writer with exception handling
 * 
 * @author azmeer
 */
public class W14bBytefileReadwriter {

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) throws IOException {
        // Specify the input and output file paths
        String inputFile = "input.jpg";
        String outputFile = "output.jpg";

        try (
                FileInputStream fileInputStream = new FileInputStream(inputFile); 
                FileOutputStream fileOutputStream = new FileOutputStream(outputFile)
            ) 
        {
            // Create a byte array to hold the data
            byte[] buffer = new byte[1024];
            int bytesRead;

            // Read from the input file and write to the output file
            while ((bytesRead = fileInputStream.read(buffer)) != -1) {
                fileOutputStream.write(buffer, 0, bytesRead);
            }

            System.out.println("File copied successfully.");

        } catch (IOException e) {
            System.out.println(e);
        }
    }
}

Buffered binary file reader/write with exception handling

Buffered streams in Java, such as BufferedInputStream and BufferedOutputStream, provide a way to improve I/O performance by using an internal buffer to reduce the number of direct read and write operations. Here’s an example using these buffered streams to copy a file. You may use this example to process text or binary files.

import java.io.*;

/**
 * uses `BufferedInputStream` and `BufferedOutputStream` 
 * to be used with internal buffer to reduce the number of direct read and write operations
 * 
 * @author azmeer
 */
public class W14dBufferedReadwriter {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // Specify the input and output file paths
        String inputFile = "input.txt";
        String outputFile = "output.txt";

        try (
                FileInputStream fileInputStream = new FileInputStream(inputFile); 
                BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream); 
                FileOutputStream fileOutputStream = new FileOutputStream(outputFile); 
                BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream)
            ) 
        {
            
            // Create a byte array to hold the data blocks
            byte[] buffer = new byte[1024];
            int bytesRead;

            // Read from the input file and write to the output file using buffered streams
            while ((bytesRead = bufferedInputStream.read(buffer)) != -1) {
                bufferedOutputStream.write(buffer, 0, bytesRead);
            }

            System.out.println("File copied successfully using buffered streams.");

        } catch (IOException e) {
            System.out.println(e);
        }
    }
}

In this example:

  1. BufferedInputStream is used to wrap the FileInputStream, and BufferedOutputStream is used to wrap the FileOutputStream. This introduces buffering to improve the efficiency of reading and writing.
  2. We create a byte array (buffer) to store the data read from the input file.
  3. The buffered streams are used within a try-with-resources block to ensure proper resource management and automatic closure of the streams.
  4. The while loop reads from the input file using the buffered input stream and writes to the output file using the buffered output stream. This is done in chunks, and the process continues until the end of the file is reached.

Java object serialization

Serialization in Java is the process of converting an object into a byte stream, which can then be saved to a file or transmitted over a network. Deserialization is the process of recreating the object from the byte stream. Java provides built-in mechanisms for object serialization and deserialization through the Serializable interface. Here’s a simple example demonstrating object serialization and deserialization:

First we need a class that can be serialised. Please note the password field. Since we label it as a ‘transient’ type, it will NOT be serialized or stored in the disk. Notice how we override the toString() method.

import java.io.*;

// A simple class to be serialized
class Person implements Serializable {

    private String name;
    private int age;
    private transient String password; //this property will NOT be serialised or saved

    public Person(String name, int age, String password) {
        this.name = name;
        this.age = age;
        this.password = password;
    }

    @Override
    public String toString() {
        return "Person{"
                + "name='" + name + '\''
                + ", age=" + age + '\''
                + ", password=" + password
                + '}';
    }
}

Here’s the code to serialization and deserialization the ‘Person’ object.

import java.io.*;

/**
 * Object Serialization example
 * 
 * @author azmeer
 */
public class W14eObjectSerialization {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // Create an instance of the Person class with default data
        Person person = new Person("John Doe", 25, "secret_code");

        // Serialize the object to a file
        serializeObject(person, "person.ser");

        // Deserialize the object from the file
        Person deserializedPerson = deserializeObject("person.ser");

        // Display the deserialized object
        System.out.println("Deserialized Person: " + deserializedPerson);
    }

    // Serialize an object to a file
    private static void serializeObject(Object obj, String filePath) {
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath))) {
            oos.writeObject(obj);
            System.out.println("Object serialized and saved to " + filePath);
        } catch (IOException e) {
            System.out.println(e);
        }
    }

    // Deserialize an object from a file
    private static Person deserializeObject(String filePath) {
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath))) {
            Object obj = ois.readObject();
            if (obj instanceof Person) {
                return (Person) obj;
            }
        } catch (IOException | ClassNotFoundException e) {
            System.out.println(e);
        }
        return null;
    }
}

In this example:

  1. The Person class implements the Serializable interface, indicating that instances of this class can be serialized.
  2. The serializeObject method takes an object and a file path, then serializes the object and saves it to the specified file.
  3. The deserializeObject method takes a file path, reads the serialized object from the file, and returns the deserialized object.
  4. In the main method, we create an instance of the Person class, serialize it to a file, then deserialize it back from the file. Finally, we print the deserialized object.

More information: Java Tutorials – Stream in java (btechsmartclass.com)


All lessons >

Share

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.