Skip to content

Java I / O

Introduction

Java provides many mechanisms for handling input/output operations. They are, among others streams and advanced serialization mechanisms:

  • streaming is provided by Java IO classes, contained in the java.io package,
  • caching mechanisms are implemented by Java NIO components in the java.nio package.

Streams and buffers

Java IO is oriented towards stream processing which consists of:

  • a certain number of bytes (one or more) are read
  • data is not cached anywhere
  • the programmer, using the data from the stream, cannot "move" on it, i.e. it is not possible to read the data once read

Java NIO is caching oriented, which means that:

  • data is first loaded into a buffer of a certain size
  • then they are put at the disposal of the programmer who can "move" around the buffer

Thread blocking

Using Java IO streams causes blocking of the currently running thread, i.e. using the read () or write () methods from the classes from the java.io package, we block further execution of the program. A thread on which such a method was executed will wait until the read data is prepared or the write data which is not fully written.

The non-blocking Java NIO mode allows a thread to request to read data from a channel and get only what is currently available, or not at all if no data is currently available. Rather than staying blocked until the data is readable, the thread can continue working with something else. The same mechanism is used for data recording. A thread in Java NIO can now manage multiple read and write channels.

Java IO

Streams of bytes

Programs use byte streams to input and output 8-bit bytes. All classes related to byte streams are derived from the InputStream and OutputStream classes, e.g. FileInputStream, FileOutputStream.

public class ByteStream {
    public static void main(String[] args) throws IOException {

        FileInputStream in = null;
        FileOutputStream out = null;

        try {
            in = new FileInputStream("user.txt");
            out = new FileOutputStream("user_output.txt");
            int c;

            while ((c = in.read()) != -1) {
                out.write(c);
            }
        } finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
    }
}
The above code fragment shows how to use streams to copy the contents of the following file to another file.

Name: John
Age: 34
Email: john@oracle.com
Phone: 1234567890

All the logic is to copy the content bit by bit as in the graphic below.

Java Byte Stream

Character streams

The Java platform stores character values using the Unicode convention. Using character streams, the data from the byte stream is translated into a local character set.

All character stream classes are derived from the Reader andWriter classes. As with byte streams, there are character stream classes that specialize in I/O files. They are FileReader andFileWriter, respectively.

public class CharacterStream {
    public static void main(String[] args) throws IOException {

        FileReader in = null;
        FileWriter out = null;

        try {
            in = new FileReader("user.txt");
            out = new FileWriter("user_output.txt");

            int c;
            int nextChar;
            while ((nextChar = in.read()) != -1) {
                out.append((char) nextChar);
            }
        } finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
    }
}

Data caching

The classes based on InputStream andOutputStream use non-buffered input/output. This means that any read or write request is handled directly by the underlying operating system. This can make the program much less efficient, as each request like that often allows disk access, network activity, or other costly operation.

In order to reduce this kind of overhead, the Java platform implements buffered I/O streams. Buffered input streams read data from an area of memory known as buffer.

The program can convert an unbuffered stream to a buffered stream using classes such as: BufferedReader,BufferedWriter, which enable caching of character streams. Classes such as BufferedInputStream andBufferedOutputStream can stream bytes.

public class BufferedStream {
    public static void main(String[] args) throws IOException {

        BufferedReader in = null;
        BufferedWriter out = null;

        try {
            in = new BufferedReader(new FileReader("user.txt"));
            out = new BufferedWriter(new FileWriter("user_output.txt"));

            String line;
            while ((line = in.readLine()) != null) {
                out.write(line);
            }
        } finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
    }
}

Data processing

In Java IO, data is read byte by byte with InputStream or Reader.

IO data processing

According to the diagram, the program only proceeds when there is new data to be read.

Java NIO

Buffers

A buffer is a data container of a specific primitive type. A buffer is a linear, finite sequence of elements of a certain primitive type. In addition to the content, the basic properties of the buffer are:

  • capacity - the number of elements it contains. The buffer capacity is never negative and never changes.
  • limit - is the index of the first element that should not be read or written. The buffer limit is never negative and never greater than its capacity.
  • position - is the index of the next read or write item. A buffer position is never negative and never exceeds its limit.

The Buffer class performs the following operations:

  • clear() - prepares the buffer for a new sequence of channel read operations or relative put operations.
  • flip() - prepares the buffer for a new sequence of write to channel or relative fetch operation.
  • rewind() - prepares the buffer to reread data that is already contained there.

Channels

A channel (from the java.nio.channels.Channel package) represents an open connection to an entity such as a hardware device, file, network socket, or program component that is capable of performing one or more different I / O operations, such as reading or write.

The channel can be open or closed. Once a channel is created, it is open and remains closed when closed. After a channel is closed, any attempt to execute an I / O operation on it will throw an exception ClosedChannelException.

Basically, channels are intended to be safe for multithreaded access, as described in the interface and class specifications that extend and implement this interface.

The most frequently used implementations include:

  • FileChannel - allows you to write and read data from a file
  • DatagramChannel - enables saving and reading data via the UDP protocol
  • SocketChannel - allows you to write and read data via the TCP protocol
  • ServerSocketChannel - allows you to listen for incoming TCP connections
public class NIOExample {

    public static void main(String[] args) throws IOException {
        RandomAccessFile file = new RandomAccessFile("user.txt", "r");
        FileChannel fileChannel = file.getChannel();
        ByteBuffer byteBuffer = ByteBuffer.allocate(512);
        while (fileChannel.read(byteBuffer) > 0) {
            byteBuffer.flip();
            while (byteBuffer.hasRemaining()) {
                System.out.print((char) byteBuffer.get());
            }
            byteBuffer.clear();
        }
        file.close();
    }
}

Data processing

NIO allows you to manage multiple channels (e.g. network connections or files) using a certain number of threads. Parsing the data, however, can be a bit more complicated than reading data from the blocking stream.

NIO handling connection

In order to successively retrieve data from the buffer, it is necessary to query it frequently, in order to collect information about data availability, which may result in a decrease in performance when reading connections.

NIO data processing

Java NIO - file operations

Java NIO introduced many simplified file handling mechanisms. These mechanisms have been placed in the java.nio.file package, and the starting point for their implementation is theFiles class.

Path

The Path interface represents the path to the file. It includes methods that can be used, among others down:

  • obtaining information about the path
  • accessing path items
  • extracting path elements

Path p1 = Paths.get("/nio/example/path");
Path p2 = Paths.get(URI.create("file:///nio/example/FileTest.java"));
Path p3 = Path.of("/tmp", "dir_a", "dir_b");

Checking files or directories

The Files class contains mechanisms that enable file verification, including:

  • Files.isExecutable(Path) - checking if a file is executable
  • Files.isReadable(Path) - checking if the file is readable
  • Files.isWritable(Path) - check if the file is writable.


Deleting files

Within the Files class, it is possible to delete files using the following methods:

  • Files.delete (Path) - the method deletes the file if it exists, or throws an exception
  • Files.deleteIfExists (Path) - the method deletes the file if it exists, otherwise does not perform any operation


Copying files

The Files class provides a mechanism for copying files or directories using the method:

  • Files.copy (Path, Path, CopyOption ...)

The CopyOption may be one of the following:

  • REPLACE_EXISTING - forces a file to be copied even if the file already exists in the specified location
  • COPY_ATTRIBUTES - includes copying file attributes
  • NOFOLLOW_LINKS - reduces copying to symbolic links


Moving files

The Files.move(Path, Path, CopyOption...) method allows you to move files that will result in an exception until the REPLACE_EXISTING flag is set.

Create file

The Files.createFile (Path, FileAttribute <?>) Method is responsible for creating files with different permissions. If the file we want to create already exists, an exception will be thrown.


Read from file

Reading from a file is performed using:

  • Files.readAllBytes (Path)
  • Files.readAllLines (Path, Charset)


Write to file

It is possible to write to the file using the following methods:

  • write(Path, byte [], OpenOption...) - write bytes
  • write(Path, Iterable <extends CharSequence>, Charset, OpenOption...) - write multiple lines, e.g. all values ​​from List <String

When specifying OpenOption, we use an implementation such as StandardOpenOption. The available options allow you to control the saving behavior, e.g. they allow you to define whether, before saving the data to the file, first delete its contents or add to the existing one.


An example of how the Files class works

public class FilesExample {

    public static void main(String[] args) throws IOException {
        Path path = Paths.get("data.txt");
        Path pathToCopy = Paths.get("data_copy.txt");

        Files.delete(path);
        Files.createFile(path);

        Files.write(path, "test data\n".getBytes(), StandardOpenOption.WRITE);
        Files.write(path, "first line\n".getBytes(), StandardOpenOption.APPEND); // APPEND dodaje tekst do istniejącej zawartości
        Files.write(path, "second line\n".getBytes(), StandardOpenOption.APPEND);

        for (String line : Files.readAllLines(path)){
            System.out.println(line);
        }

        Files.copy(path, pathToCopy);
        Files.delete(pathToCopy);
    }
}

Serialization and deserialization

Serialization is a mechanism that converts the state of an object into a stream of bytes. Deserialization, on the other hand, is the inverse process that allows you to recover the state of an object in memory from a stream of bytes.

The resulting byte stream is platform independent, so an object serialized on one platform can be successfully deserialized on another platform.

Serialization and deserialization

Serializable

The Serializable interface enables the serialization and deserialization of objects using, incl. the classes ObjectInputStream and ObjectOutputStream, which contain a high-level API for serializing and deserializing objects.

import java.io.Serializable;

public class Movie implements Serializable {
    private int id;
    private String title;
    private String type;

    public Movie(int id, String title, String type) {
        this.id = id;
        this.title = title;
        this.type = type;
    }

    //getters and setters
}

Without implementing the java.io.Serializable interface, the above classes will not be able to properly serialize and deserialize theMovie class object, which will result in a java.io.NotSerializableException exception. Also note that the Serializable interface does not require us to implement any method.

ObjectInputStream

The ObjectInputStream class contains a method that enables the object to be serialized and written to the stream:

public final void writeObject(Object x) throws IOException

Przykładowa serializacja wygląda jak poniżej:

        try {
            Movie movieToSerialize = new Movie(1123, "Star Wars", "Start Wars IX");
            FileOutputStream fileOutputStream = new FileOutputStream("movies.txt");
            ObjectOutputStream out = new ObjectOutputStream(fileOutputStream);
            out.writeObject(movieToSerialize);
            out.flush();
            out.close();
        } catch (Exception e) {
            System.err.println(e);
        }

ObjectOutputStream

The ObjectOutputStream class contains a method that allows you to deserialize an object and read it from the stream:

public final Object readObject() throws IOException, ClassNotFoundException

An example deserialization looks like this:

        try {
            FileInputStream fileIn = new FileInputStream("movies.txt");
            ObjectInputStream in = new ObjectInputStream(fileIn);
            Movie movieToDeserialize = (Movie) in.readObject();
            in.close();
            fileIn.close();
            System.out.println(movieToDeserialize);
        } catch (Exception e) {
            System.err.println(e);
        }