Java IO – 1 [ Overview ]

An input/output in Java consists of exchanging data between the program and another source which can be : memory, a file, or the program itself.

To achieve this, Java uses what is called a stream (which means flow) between the source and the destination of the data.

Any input/output operation in Java follows the following pattern :

  • Opening a stream;
  • Reading or writing data;
  • Closing the stream.

As you will have understood, java.io provides all the classes necessary for creating, reading, writing and processing streams.

What You Need

  • About 16 minutes
  • A favorite text editor or IDE
  • Java 8 or later

1. Overview

The package java.io is composed of many classes, but they can be divided into 4 categories, depending on :

  • Reading operation or Writing operation;
  • Textual data or Binary data.

We can place them on a graph with 4 quadrants, each one is an abstract class in java.io package :

ReadWrite
TextualReaderWriter
BinaryInputStreamOutputStream

These abstract classes are then implemented by different specialized concrete classes.

From the perspective of data source or operations, those classes can be divided into : file, array, pipe, data, buffer, print, object and string :

FileReadWrite
TextualFileReaderFIleWriter
BinaryFileInputStreamFileOutputStream
ArrayReadWrite
TextualCharArrayReaderCharArrayWriter
BinaryByteArrayInputStreamByteArrayOutputStream
PipeReadWrite
TextualPipedReaderPipedWriter
BinaryPipedInputStreamPipedOutputStream
BufferReadWrite
TextualBufferedReaderBufferedWriter
BinaryBufferedInputStreamBufferedOutputStream
DataReadWrite
BinaryDataInputStreamDataOutputStream
ObjectReadWrite
BinaryObjectInputStreamObjectOutputStream
StringReadWrite
TextualStringReaderStringWriter
PrintWrite
TextualPrintWriter
BinaryPrintStream

There are also two classes that bridge the universe of binary data and textual data :

  • InputStreamReader : read binary data into textual data;
  • OutputStreamWriter : write textaul data into binary data.
ReadWrite
TextualReaderWriter
InputStreamReader
(InputStream => Reader)
OutputStreamWriter
(Writer => OutputStream)
BinaryInputStreamOutputStream

2. File

2.1 FileInputStream

FileInputStream is meant for reading streams of raw bytes such as image data.

Below is an example to read class file into byte array and check its size :

import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;

public class FileInputStreamTest {
    public static void main(String[] args) throws IOException {
        URL url = FileInputStreamTest.class.getResource("FileInputStreamTest.class");

        String path = url.getPath();

        try (FileInputStream fis = new FileInputStream(path)) {
            byte[] bytes = fis.readAllBytes();

            System.out.println("File Size = " + bytes.length);
        }
    }
}

The output of above code snippet is below :

File Size = 1730

When using ls command to check file size, we can see that it is the same :

ls -l FileInputStreamTest.class
-rw-rw-r-- 1 ovo ovo 1730 dec.  17 18:11 FileInputStreamTest.class

2.2 FileReader

FileReader is meant for reading character files.

Below is an example to read text file :

import java.io.FileReader;
import java.io.IOException;

public class FileReaderTest {
    public static void main(String[] args) throws IOException {
        String path = "/home/ovo/github/BlogTests/README.md";
        try (FileReader reader = new FileReader(path)) {
            char[] buffer = new char[1024];

            while (reader.read(buffer) != -1) {
                StringBuilder sb = new StringBuilder();
                sb.append(buffer);
                System.out.println(sb.toString());
            }
        }
    }
}

Below is the output of above code snippet :

# BlogTests

This repo contains all the tests in my blog.

When using cat command to display the text file content, we can see that it is the same :

cat README.md 

# BlogTests

This repo contains all the tests in my blog.

2.3 FileOutputStream

FileOutputStream is meant for writing streams of raw bytes such as image data.

Below is an example to write 1024 bytes into a file :

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

public class FileOutputStreamTest {
    public static void main(String[] args) throws IOException {
        String path = "/home/ovo/github/BlogTests/greeting";

        try (FileOutputStream fos = new FileOutputStream(path)) {
            byte[] bytes = new byte[1024];

            fos.write(bytes);
        }
    }
}

When using ls command to check the created file, we can see that its size is 1024 bytes exactly :

ls -l greeting 
-rw-rw-r-- 1 ovo ovo 1024 dec.  20 11:04 greeting

2.4 FileWriter

FileWriter is meant for writing streams of characters into file.

Below is an example to write a String into a file :

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

public class FileWriterTest {
    public static void main(String[] args) throws IOException {
        String path = "/home/ovo/github/BlogTests/greeting";
        try (FileWriter writer = new FileWriter(path)) {
            writer.write("hello world");
        }
    }
}

When using cat command to display the content of the file, we can see that it is exactly the same as we were writing in above code snippet :

cat greeting
hello world

3. Array

3.1 ByteArrayInputStream

A ByteArrayInputStream contains an internal buffer that contains bytes that may be read from the stream.

Below is an example to read data from byte arrays :

import java.io.ByteArrayInputStream;
import java.io.IOException;

public class ByteArrayInputStreamTest {
    public static void main(String[] args) throws IOException {
        byte[] greeting = "hello world".getBytes();
        try (ByteArrayInputStream bais = new ByteArrayInputStream(greeting)) {
            byte[] bytes = bais.readAllBytes();

            System.out.println(new String(bytes));
        }
    }
}

The output of above code snippet is below :

hello world

3.2 ByteArrayOutputStream

ByteArrayOutputStream implements an output stream in which the data is written into a byte array. The buffer automatically grows as data is written to it. The data can be retrieved using toByteArray() and toString().

Below is an example to write byte data into ByteArrayOutputStream buffer and then retrieve them from this buffer :

import java.io.ByteArrayOutputStream;
import java.io.IOException;

public class ByteArrayOutputStreamTest {
    public static void main(String[] args) throws IOException {
        byte[] bytes = "hello world".getBytes();
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            baos.write(bytes);

            byte[] allBytes = baos.toByteArray();

            System.out.println(new String(allBytes));
        }
    }
}

Below is the output of above code snippet :

hello world

3.3 CharArrayReader

CharArrayReader implements a character buffer that can be used as a character input stream.

Below is an example to read data from char array :

import java.io.CharArrayReader;
import java.io.IOException;

public class CharArrayReaderTest {
    public static void main(String[] args) throws IOException {
        String s = "hello world";
        try (CharArrayReader reader = new CharArrayReader(s.toCharArray())) {
            int data;
            while ((data = reader.read()) != -1) {
                System.out.print((char) data);
            }
            System.out.println();
        }
    }
}

The output of above code snippet is below :

hello world

3.4 CharArrayWriter

CharArrayWriter implements a character buffer that can be used as an Writer.

The buffer automatically grows when data is written to the stream.

The data can be retrieved using toCharArray() and toString().

Below is an example to write data into buffer and fetch data into char array :

import java.io.CharArrayWriter;
import java.io.IOException;

public class CharArrayWriterTest {
    public static void main(String[] args) throws IOException {
        String s = "hello world";
        try (CharArrayWriter writer = new CharArrayWriter()) {
            writer.write(s);

            char[] chs = writer.toCharArray();

            System.out.println(chs);
        }
    }
}

Below is the output of above code snippet :

hello world

4. Pipe

4.1 PipedInputStream and PipedOutputStream

PipedInputStream and PipedOutputStream connect one to another in order to create a communications pipe to exchange byte data.

The piped output stream is the sending end of the pipe and the piped input stream is the receiving end of the pipe.

Typically, data is written to a PipedOutputStream object by one thread and data is read from the connected PipedInputStream by some other thread.

Below is an example to create a communications pipe between two threads :

import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

public class PipedInputOutputStreamTest {
    public static void main(String[] args) throws IOException {
        final PipedInputStream pis = new PipedInputStream();
        final PipedOutputStream pos = new PipedOutputStream();

        pos.connect(pis);

        Thread t1 = new Thread(() -> {
            try {
                pos.write("hello world".getBytes());
            } catch (IOException e) {
                throw new RuntimeException(e);
            } finally {
                try {
                    pos.close();
                } catch (IOException e1) {
                    throw new RuntimeException(e1);
                }
            }
        });

        t1.setUncaughtExceptionHandler((Thread t, Throwable e) -> {
            e.printStackTrace();
        });

        Thread t2 = new Thread(() -> {
            try {
                byte[] bytes = pis.readAllBytes();

                System.out.println(new String(bytes));
            } catch (IOException e1) {
                throw new RuntimeException(e1);
            } finally {
                try {
                    pis.close();
                } catch (IOException e1) {
                    throw new RuntimeException(e1);
                }
            }
        });

        t2.setUncaughtExceptionHandler((Thread t, Throwable e) -> {
            e.printStackTrace();
        });

        t1.start();
        t2.start();
    }
}

Below is the output of above code snippet :

hello world

4.2 PipedReader and PipedWriter

PipedReader and PipedWriter connect one to another in order to create a communications pipe to exchange character data.

The piped writer is the sending end of the pipe and the piped reader is the receiving end of the pipe.

Typically, character data is written to a PipedWriter object by one thread and character data is read from the connected PipedReader by some other thread.

Below is an example to create a communications pipe between two threads :

import java.io.IOException;
import java.io.PipedReader;
import java.io.PipedWriter;

public class PipedReaderWriterTest {
    public static void main(String[] args) throws IOException {
        PipedReader reader = new PipedReader();
        PipedWriter writer = new PipedWriter();

        writer.connect(reader);

        Thread t1 = new Thread(() -> {
            try {
                writer.write("hello world");
            } catch (IOException e) {
                throw new RuntimeException(e);
            } finally {
                try {
                    writer.close();
                } catch (IOException e1) {
                    throw new RuntimeException(e1);
                }
            }
        });

        t1.setUncaughtExceptionHandler((Thread t, Throwable e) -> {
            e.printStackTrace();
        });

        Thread t2 = new Thread(() -> {
            try {
                char[] chs = new char[1024];
                reader.read(chs);
                System.out.println(chs);
            } catch (IOException e1) {
                throw new RuntimeException(e1);
            } finally {
                try {
                    reader.close();
                } catch (IOException e1) {
                    throw new RuntimeException(e1);
                }
            }
        });

        t2.setUncaughtExceptionHandler((Thread t, Throwable e) -> {
            e.printStackTrace();
        });

        t1.start();
        t2.start();
    }
}

The output of above code snippet is below :

hello world

5. Print

5.1 PrintStream

A PrintStream provides methods to write data to another output stream.

It automatically flushes the data so there is no need to call flush method.

Moreover, its methods don’t throw IOException, instead, exceptional situations merely set an internal flag that can be tested via the checkError method.

Below is an example to get the exception stack trace by using print stream :

 1import java.io.ByteArrayOutputStream;
 2import java.io.IOException;
 3import java.io.PrintStream;
 4
 5public class PrintStreamTest {
 6    public static void main(String[] args) throws IOException {
 7        try {
 8            System.out.println(100 / 0);
 9        } catch (Exception e) {
10            try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
11                    PrintStream ps = new PrintStream(baos)) {
12                e.printStackTrace(ps);
13                System.out.println("Stack Trace of Exception is :");
14                System.out.println(baos.toString());
15            }
16        }
17    }
18}

The output of above code snippet is below :

Stack Trace of Exception is :
java.lang.ArithmeticException: / by zero
        at PrintStreamTest.main(PrintStreamTest.java:8)

5.2 PrintWriter

Like PrintStream, a PrintWriter provides methods to write data to another output stream as well, but only to a text-output stream.

Below is an example to writer data into file by using print writer :

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;

public class PrintWriterTest {
    public static void main(String[] args) throws FileNotFoundException {
        String path = "/home/ovo/github/BlogTests/greeting";

        try (PrintWriter pw = new PrintWriter(new FileOutputStream(path))) {
            pw.println("hello world");
        }
    }
}

When using cat command to display the file content, we can see that it is the same as in above code snippet :

cat greeting
hello world

6. Data

DataInputStream and DataOutputStream

A DataOutputStream lets an application write primitive Java data types to an output stream in a portable way.

An application can then use a DataInputStream to read the data back in.

Below is an example to use DataOutputStream to write a data table to a file, then use DataInputStream to read that file :

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

public class DataInputOutputStreamTest {
    public static void main(String[] args) throws IOException {
        String path = "/home/ovo/github/BlogTests/person";

        try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(path))) {
            dos.writeUTF("tom");
            dos.writeInt(18);
            dos.writeBoolean(true);

            dos.writeUTF("jerry");
            dos.writeInt(16);
            dos.writeBoolean(true);
        }

        try (DataInputStream dis = new DataInputStream(new FileInputStream(path))) {
            while (dis.available() > 0) {
                String name = dis.readUTF();
                int age = dis.readInt();
                boolean male = dis.readBoolean();

                System.out.print(name);
                System.out.print(" ");
                System.out.print(age);
                System.out.print(" ");
                System.out.print(male);
                System.out.println();
            }
        }
    }
}

The output of above code snippet is below :

tom 18 true
jerry 16 true

7. Object

ObjectInputStream and ObjectOutputStream

ObjectInputStream and ObjectOutputStream are enhanced version of DataInputStream and DataOutputStream.

Not only they write/read primitive data, but also they write/read java objects.

So ObjectInputStream and ObjectOutputStream are often used to serialize/deserialize java objects to/from file or socket.

Only objects that support the java.io.Serializable interface can be written/read to/from streams.

Below is an example to use ObjectOutputStream to write java objects to a file, then use ObjectInputStream to read that file :

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class ObjectInputOutputStreamTest {
    private static class Person implements Serializable {
        private String name;
        private int age;
        private boolean male;

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

        @Override
        public String toString() {
            return "Person [age=" + age + ", male=" + male + ", name=" + name + "]";
        }
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        String path = "/home/ovo/github/BlogTests/person";

        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path))) {
            oos.writeInt(2);

            oos.writeObject(new Person("tom", 18, true));
            oos.writeObject(new Person("jerry", 16, true));
        }

        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path))) {
            int num = ois.readInt();

            for (int i = 0; i < num; i++) {
                Person p = (Person) ois.readObject();
                System.out.println(p);
            }
        }
    }
}

Below is the output of above code snippet :

Person [age=18, male=true, name=tom]
Person [age=16, male=true, name=jerry]

8. Buffer

8.1 BufferedInputStream

A BufferedInputStream automatically takes binary data from the original input stream and stores them in buffer.

We often use it to improve program performance.

Below is an example to read file content by using a file input stream enveloped by a buffered input stream :

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class BufferedInputStreamTest {
    public static void main(String[] args) throws FileNotFoundException, IOException {
        String path = "/home/ovo/github/BlogTests/README.md";
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(path))) {
            byte[] bytes = bis.readAllBytes();
            System.out.println(new String(bytes));
        }
    }
}

Below is the output of above code snippet :

# BlogTests

This repo contains all the tests in my blog.

8.2 BufferedReader

A BufferedReader reads text from a character-input stream, buffering characters so as to provide for the efficient reading of characters, arrays, and lines.

The buffer size may be specified, or the default size may be used which is large enough for most purposes.

Below is an example to read file content by using a file reader enveloped by a buffered reader :

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class BufferedReaderTest {
    public static void main(String[] args) throws IOException {
        String path = "/home/ovo/github/BlogTests/README.md";

        try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
            reader.lines().forEach(System.out::println);
        }
    }
}

Below is the output of above code snippet :

# BlogTests

This repo contains all the tests in my blog.

8.3 BufferedOutputStream

A BufferedOutputStream is used for the purpose of simplifying writing binary data to another output stream and improving program performance.

Below is an example to write string byte array to file by using a file output stream envoloped by a buffered output stream :

import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class BufferedOutputStreamTest {
    public static void main(String[] args) throws FileNotFoundException, IOException {
        String path = "/home/ovo/github/BlogTests/greeting";
        try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(path))) {
            bos.write("hello world".getBytes());
        }
    }
}

When using cat command to check the content of the file, we can see that it is exactly the same as in above code snippet :

cat greeting 
hello world

8.4 BufferedWriter

A BufferedWriter writes text to a character-output stream, buffering characters so as to provide for the efficient writing of single characters, arrays, and strings.

The buffer size may be specified, or the default size may be accepted which is large enough for most purposes.

Below is an example to write text to file by using a file writer enveloped by a buffered writer :

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

public class BufferedWriterTest {
    public static void main(String[] args) throws IOException {
        String path = "/home/ovo/github/BlogTests/greeting";
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(path))) {
            writer.write("hello");
            writer.newLine();
            writer.write("bonjour");
            writer.newLine();
        }
    }
}

When using cat command to check the content of the file, we can see that it is exactly the same as in above code snippet :

cat greeting 
hello
bonjour

9. String

9.1 StringReader

A StringReader is used to read a String as a character input stream.

Below is an example to read a String by using a string reader envoloped by a buffered reader :

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;

public class StringReaderTest {
    public static void main(String[] args) throws IOException {
        String content = """
                hello java
                bonjour java
                """;

        try (BufferedReader reader = new BufferedReader(new StringReader(content))) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        }
    }
}

The output of above code snippet is below :

hello java
bonjour java

9.2 StringWriter

A StringWriter is a character stream that collects its output in a string buffer, which can then be used to construct a string.

Below is an example to write exception stack trace into a String by using a string writer and a print writer :

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;

public class StringWriterTest {
    public static void main(String[] args) throws IOException {
        try {
            int div = 1 / 0;
        } catch (Exception e) {
            try (StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw)) {
                e.printStackTrace(pw);
                System.out.println("Exception caught with stack trace = ");
                System.out.println(sw.toString());
            }
        }
    }
}

Below is the output of above code snippet :

Exception caught with stack trace = 
java.lang.ArithmeticException: / by zero
        at StringWriterTest.main(StringWriterTest.java:8)

10. Bridge

10.1 InputStreamReader

An InputStreamReader is a bridge from byte streams to character streams.

It reads bytes and decodes them into characters using a specified charset.

The charset that it uses may be specified by name or may be given explicitly, or the platform’s default charset may be accepted.

Below is an example to read string bytes and to decode into utf-8 :

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;

public class InputStreamReaderTest {
    public static void main(String[] args) throws IOException {
        byte[] bytes = "this is a test!".getBytes();

        try (InputStreamReader reader = new InputStreamReader(new ByteArrayInputStream(bytes),
                StandardCharsets.UTF_8)) {
            char[] buffer = new char[1024];

            StringBuilder sb = new StringBuilder();

            while (reader.read(buffer) != -1) {
                sb.append(buffer);
            }

            System.out.println(sb.toString());
        }
    }
}

The output of above code snippet is below :

this is a test!

10.2 OutputStreamWriter

An OutputStreamWriter is a bridge from character streams to byte streams.

Characters written to it are encoded into bytes using a specified charset.

The charset that it uses may be specified by name or may be given explicitly, or the platform’s default charset may be accepted.

Below is an example to write utf-8 encoded string into a file :

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;

public class OutputStreamWriterTest {
    public static void main(String[] args) throws IOException {
        String path = "/home/ovo/github/BlogTests/greeting";

        try (OutputStreamWriter writer = new OutputStreamWriter(
            new FileOutputStream(path), StandardCharsets.UTF_8)) {
            writer.write("hello world");
            writer.write(System.getProperty("line.separator"));
        }
    }
}

When using cat command to check the content of the file, we can see that it is the same as in above code snippet :

cat greeting 
hello world