java.io
package
java.io is a package in the Java Standard Library that provides classes and interfaces for system input and output through data streams, serialization, and the file system. It includes a wide range of classes that facilitate reading from and writing to different data sources such as files, network connections, and memory buffers.
Why Java I/O
- How to work with files and directories?
- How to read and write data in binary and text format?
- How to read from and write to data sources like files, console, or network?
- What are the performance implications of reading large files byte-by-byte, all at once, and in chunks?
- How to serialize and deserialize data?
Creating Files 📝 and Directories 📁
To create new files or directories (also known as folders), we can use the java.io.File
class.
Create a File Example 📝
To create a new file in Java, you can use the createNewFile()
method of the java.io.File
class. This method returns true
if the file was created successfully, and false
if the file already exists.
The example below creates an empty file in the current working directory, which is the directory from which the application is executed.
App.java
|
|
Create a Directory Example 📁
To create a new directory in Java, you can use the mkdir()
method of the java.io.File
class. This method returns true
if the directory was created successfully, and false
if the directory already exists.
The example below creates a new directory in the current working directory, which is the directory from which the application is executed.
App.java
|
|
Listing Directories Example
To list the content of a directory in Java, you can use the list()
method of the java.io.File
class. This method returns an array of files and subdirectories.
The example below lists the content of a directory.
App.java
|
|
Stream
A stream is a sequence of data that flows in and out of a program. Streams are used to read data from sources like files or the network, and to write data to destinations like files or the screen.
In Java, a stream represents a flow of data with a writer at one end and a reader at the other.
- In the Java API, a source from which one can read bytes is called an input stream.
- The bytes can come from a file, a network connection, or an array in memory.
- In the Java API, a destination to which one can write bytes is called an output stream.
- The bytes can be written to a file, a network connection, or an array in memory.
Type of Streams
Stream Type | Description | Characteristics | Examples |
---|---|---|---|
Byte Streams | Deals with raw binary data (e.g., images, audio, video) or text in non-default encodings | - Operates on 8-bit bytes - Works with raw bytes, so you need to handle encoding yourself - Used for binary files such as images, audio, video, or binary data and text in non-default encodings - Can read text files and handle any character encoding | InputStream , OutputStream |
Character Streams | Deals with text data (characters, strings) | - Operates on 16-bit characters (Unicode) - Handles character encoding using the platform’s default character encoding (e.g., UTF-8) - Used for text files and strings | Reader , Writer |
Standard Streams | Deals with standard input, output, and error | - Operates on 8-bit bytes - Does not handle character encoding automatically - There are three standard streams: 1. System.in for reading input (e.g., keyboard)2. System.out for writing output (e.g., screen)3. System.err for writing error messages (e.g., screen) | System.in , System.out , System.err |
1) Byte Streams
1.1) Byte InputStream Class Hierarchy
1.2) Byte OutputStream Class Hierarchy
2) Character Streams
2.1) Reader Class Hierarchy
2.2) Writer Class Hierarchy
File Handling with Streams
Reading from a file
To read from a file use either FileInputStream
or FileReader
depending on what type of data you want to read. If you’re reading raw bytes, use FileInputStream
. If you want to read stream of characters, use FileReader
.
Reading Raw Bytes (FileInputStream
) Example
We will read an audio file using FileInputStream
. The audio file can be downloaded here and has a size of 83KB (84960 bytes).
App.java
|
|
Total bytes read: 84960
Reading Characters (FileReader
) Example
We will read a text file using FileReader
without buffering. The text file content is listed below contains UTF-8 encoded text including special characters and emojis.
sample.txt
App.java
|
|
Explanation:
FileReader.read()
can handle different Unicode characters:
For example, the letters in the Arabic word “مرحبا” are returned as single values as follows:
Character: م, Unicode: U+0645 (decimal: 1605)
Character: ر, Unicode: U+0631 (decimal: 1585)
Character: ح, Unicode: U+062D (decimal: 1581)
Character: ب, Unicode: U+0628 (decimal: 1576)
Character: ا, Unicode: U+0627 (decimal: 1575)
Writing to a file
To write to a file use either FileOutputStream
or FileWriter
depending on what type of data you want to write. If you’re writing raw bytes, use FileOutputStream
. If you want to write stream of characters to a file, use FileWriter
.
Writing Raw Bytes (FileOutputStream
) Example
In the following example, a string message is converted into a sequence of bytes. These bytes are written to the file using FileOutputStream
. Because we are writing the data in ASCII format, each character in the string is converted to its corresponding ASCII byte value before being written to the file.
If you open the generated file in any text editor, you will see the original message as plain text since the default character encoding for most text editors is UTF8, which is backward compatible with ASCII.
App.java
|
|
Writing Characters (FileWriter
) Example
In the following example, a string message is written to a file using FileWriter
. FileWriter
handles character encoding automatically, converting each character in the string to its corresponding byte representation in the specified encoding (UTF-8 by default).
App.java
|
|
Buffered Streams
Problem: Reading byte-by-byte is slow 🐌 🐢, loading all at once = out of memory error 💥 💣
Why?: 💾 Reading from disk for every byte = slow 🐌 🐢
What?: 🪣 read chunks of data from disk into a reserved area of memory called buffer memory.
How?: Both byte and character streams can be buffered 🚀
- Byte streams:
BufferedInputStream
/BufferedOutputStream
- Character streams:
BufferedReader
/BufferedWriter
- Byte streams:
Buffered Byte Streams: BufferedInputStream
and BufferedOutputStream
Example
Below is an example that copies half of the bytes of a binary file (audio file) to a new file using buffered byte streams (BufferedInputStream
and BufferedOutputStream
).
App.java
|
|
Buffered Character Streams: BufferedReader
and BufferedWriter
Example
Below is an example that reads the first 1000 lines from the input file (Moby Dick) and writes them to a new file using both buffered readers and writers.
App.java
|
|
Connecting Streams
Connecting streams in Java involves chaining multiple streams together to achieve more complex I/O operations. This technique allows us to combine the functionality of different streams, such as reading stream of bytes from a file, buffering the input, and then processing the data.
Connecting Streams Example
In this example, we will read a text file using FileInputStream
, wrap it with InputStreamReader
to handle character encoding (Windows-1256 for Arabic), and then use BufferedReader
to buffer the input and read lines efficiently. Finally, we will print each line of the file to the console.
- Problem: We need to read a text file encoded in
Windows-1256
for Arabic, written on a legacy Windows system. Using character streams likeReader
orFileReader
won’t work because they assume the platform’s default character encoding, which is often UTF-8. Therefore, we need to use byte streams to handle the encoding correctly. - Solution: Use
FileInputStream
to read the raw bytes,InputStreamReader
to convert bytes to characters with the specified encoding Windows-1256, andBufferedReader
to efficiently read chunks of data and storing them in a buffer to reduce the number of I/O operations.
arabic-Windows-1256.txt
App.java
|
|
Output in UTF-8
Note: In Java 11, the constructor of
FileReader
can accept [standard charsets supported by the Java platform](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/FileReader.html#%3Cinit%3E(java.io.File,java.nio.charset.Charset). For example, you can useFileReader fr = new FileReader("input.txt", Charset.forName("UTF-8"));
. However, for more complex scenarios involving different character encodings, consider usingFileInputStream
,InputStreamReader
andBufferedReader
together.
Serialization and Deserialization
- Serialization: Converting an object into a stream of bytes.
Student s
-> 📦 ->01001010
- Deserialization: Converting a stream of bytes back into an object.
01001010
-> 📭 ->Student s
Serialization
Serialization is the process of converting an object into a stream of bytes, so it can be saved to a file, database, or transferred over a network.
How to do Serialization in Java
- Implement the
Serializable
interface in the class to be serialized.
- Create an instance of the class.
- Use
FileOutputStream
to create a file output stream. - Wrap the
FileOutputStream
with anObjectOutputStream
. - Call the
writeObject()
method ofObjectOutputStream
to serialize the object.
- Handle exceptions such as
IOException
.
Serialization Example
To serialize an instance of the Student class into a stream of bytes and save it to a file, we need to ensure that the Student class implements the Serializable
interface. This allows the Java serialization mechanism to convert the Student object into a byte stream, which can then be written to a file for persistent storage.
Student.java
|
|
Next, we create an instance of the class and use FileOutputStream
to create a file output stream.
We wrap the FileOutputStream
with an ObjectOutputStream
and call the writeObject()
to serialize the object.
Main.java
|
|
Deserialization
The reverse process of serialization is called deserialization. Deserialization is the process of converting a stream of bytes back into a copy of the original Java object. This allows the object to be reconstructed in memory, making it possible to read data from files, databases, or network sources and use it within your application.
Deserialization Example
Similar to serialization, when we want to deserialize a stream of bytes into an object, we will have the Student class implement the Serializable
interface.
the Next, we create an instance of the class and use FileInputStream
to create a file input stream.
We wrap the FileInputStream
with an ObjectInputStream
and call the readObject()
to deserialize the byte stream into an object.
Main.java
|
|
Conclusion
Java’s I/O API may initially seem complex compared to some programming languages like Python and Ruby. However, many other languages, such as C++ and C#, share similar concepts for handling I/O and working with streams of bytes and characters. Once the core concepts and interactions between different classes are understood, working with Java’s I/O API becomes much more easier.