Chapter 11 Java Input & Output (I & O)
Understanding Java I/O¶
Java I/O, which stands for Input and Output, is a fundamental concept in Java that allows us to interact with data sources. These sources could be a simple text file, a database, a network connection, or something as complex as a web service.
Imagine you want to read a file or send a message over the network. For such scenarios, we use the Java I/O system. In Java, these data interactions are carried out using the concept of 'streams', which makes I/O operations efficient by handling data as a sequence of bytes.
The java.io package encompasses all the necessary classes to perform input and output operations.
Byte Streams vs Character Streams¶
In Java, we can categorize streams into two types: Byte streams and Character streams.
-
Byte Streams: Byte streams read or write data byte by byte, making them the most fundamental, as they deal with the smallest unit of data. If you're dealing with binary data like images, you would use byte streams. Java defines two types of Byte streams:
InputStreamandOutputStream. -
Character Streams: In contrast, Character streams deal with characters and use Unicode, making them excellent for handling text data in any human language. Java provides
ReaderandWriteras the two abstract classes for character streams.
Reading and Writing with Java I/O Streams¶
There are numerous classes in Java to support input and output streams. For binary data, you can use InputStream or OutputStream. In contrast, character data can be handled by Reader or Writer.
Below is a basic example of reading with an InputStream:
// Create an InputStream to read a file
InputStream is = new FileInputStream("path/to/your/file");
// Use the available() method to know how many bytes are available to read
int size = is.available();
// Read and print each byte
for(int i = 0; i < size; i++) {
System.out.print((char)is.read());
}
// Always close the stream when done to free resources
is.close();
Similarly, you can write to a file using OutputStream:
// Create an OutputStream to write to a file
OutputStream os = new FileOutputStream("path/to/your/file");
// Define the data to write
String data = "This is the data to write in file.";
// Write data and convert it to bytes
os.write(data.getBytes());
// Close the stream
os.close();
File Handling in Java¶
When we talk about File I/O, we are referring to the reading from or writing to a file. Java's I/O library offers several classes to handle files, allowing us to create, delete, get the properties of a file, or even manipulate directories.
Reading Files¶
You can read from a file using the FileInputStream class from the java.io package. When dealing with text data, it's common to wrap it in a BufferedReader, which makes it easier to handle the data line by line.
// Create a FileInputStream
FileInputStream fis = new FileInputStream("path/to/your/file");
// Wrap it in a BufferedReader for easier text processing
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
String line;
// Use the readLine() method to read the file line by line
while((line = br.readLine()) != null) {
System.out.println(line);
}
// Close the stream
br.close();
Writing Files¶
Writing to a file is straightforward using the FileOutputStream class. Similar to reading files, you can wrap a FileOutputStream in a BufferedWriter for more convenient text writing.
// Create a FileOutputStream
FileOutputStream fos = new FileOutputStream("path/to/your/file");
// Wrap it in a BufferedWriter
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fos));
// Write lines of text to the file
bw.write("This is the first line.");
bw.newLine(); // This adds a new line separator
bw.write("This is the second line.");
// Always close the stream
bw.close();
Working with Directories¶
Java also allows manipulation of directories, such as creating, deleting, and verifying the existence of directories.
// Create a File object for the directory
File directory = new File("path/to/your/directory");
// Use mkdir() to create the directory
directory.mkdir();
// Use delete() to delete the directory
directory.delete();
// Use exists() to check if the directory exists
boolean exists = directory.exists();
Buffering and Performance in Java I/O¶
Buffering data can drastically speed up I/O operations. Java provides buffered I/O streams: BufferedInputStream, BufferedOutputStream, BufferedReader, and BufferedWriter. These classes read or write data in chunks (a buffer), which is much faster than handling one byte or character at a time.
Buffering is especially useful when working with large files, where reading or writing byte by byte would be inefficient.
Working with FileChannel and ByteBuffer (Java 20 feature)¶
Java 20 has introduced improvements to the FileChannel and ByteBuffer classes. FileChannel provides a connection to a file and enables reading, writing, mapping, and manipulating the file. ByteBuffer is a container for data of primitive types.
In the following
example, we use FileChannel to read a file and write the content to another file.
// Open the file and get a readable channel
try (FileChannel inChannel = FileChannel.open(Paths.get("path/to/input/file"), StandardOpenOption.READ);
// Open the file and get a writable channel
FileChannel outChannel = FileChannel.open(Paths.get("path/to/output/file"), StandardOpenOption.WRITE, StandardOpenOption.CREATE)) {
// Create a ByteBuffer with a capacity of 1024 bytes
ByteBuffer buffer = ByteBuffer.allocate(1024);
// Read data from the inChannel into the buffer
while (inChannel.read(buffer) > 0) {
// Flip the buffer to prepare it for writing
buffer.flip();
// Write data from the buffer into the outChannel
outChannel.write(buffer);
// Clear the buffer to prepare it for the next read
buffer.clear();
}
}
New I/O (NIO) in Java¶
New I/O (NIO) is a collection of Java programming language APIs that offer features for intensive I/O operations. It was introduced with the J2SE 1.4 release of Java by Sun Microsystems to complement an existing standard I/O. NIO was developed to allow Java programmers to implement high-speed I/O without having to write custom native code. NIO moves the time-consuming I/O activities (like filling, draining buffers, etc.) back into the operating system, thus allowing for a great increase in speed.
Understanding File I/O using NIO¶
Java NIO's file capabilities are extensive and offer greater control than traditional I/O. It also includes methods for file and directory manipulation.
NIO uses Channels and Buffers for data manipulation. A Channel provides an open connection to an I/O device such as a file or socket, and a Buffer is a container for data.
Path path = Paths.get("path/to/your/file");
Charset charset = Charset.forName("US-ASCII");
try (BufferedReader reader = Files.newBufferedReader(path, charset)) {
String line = null;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException x) {
System.err.format("IOException: %s%n", x);
}
Networking with Java I/O¶
Java I/O is not only about file handling; it also supports network programming through the java.net package. This package includes classes for socket programming, which allows for interprocess communication, enabling you to write client-server applications.
A simple server can be set up with a ServerSocket, which listens for incoming client connections:
// Create a server socket
ServerSocket serverSocket = new ServerSocket(8080);
// Wait for a client connection
Socket clientSocket = serverSocket.accept();
// Get the input and output streams
InputStream in = clientSocket.getInputStream();
OutputStream out = clientSocket.getOutputStream();
// Use BufferedReader to read text from the client
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String message = reader.readLine();
System.out.println("Received from client: " + message);
// Use PrintWriter to send text back to the client
PrintWriter writer = new PrintWriter(out, true);
writer.println("Message received.");
// Always remember to close the socket
clientSocket.close();
Understanding Java I/O operations is essential for almost any Java application. It's fundamental for interacting with databases, reading user input, saving files, networking, and many other tasks. A good understanding of Java I/O will make your work more efficient and open up many possibilities for your applications.