Understanding Java IO Streams: BIO, Byte and Character Streams, and Practical Read/Write Examples
This article introduces Java's three generations of IO frameworks, explains the differences between blocking BIO, NIO, and NIO2, details byte and character streams, and provides complete example programs for writing to and reading from text files using try‑with‑resources and stream conversions.
Introduction
In computer science, I/O (Input/Output) refers to the transfer of data between a program and external sources such as files or networks. This tutorial focuses on Java's I/O system, especially the simplest synchronous blocking I/O (BIO) before moving on to NIO and AIO.
Java I/O Frameworks
Java has three generations of I/O APIs:
First generation: Blocking I/O (BIO) based on the java.io package.
Second generation: NIO (non‑blocking, multiplexed I/O) offering higher‑performance operations.
Third generation: NIO2/AIO (asynchronous I/O) for true async processing.
This article concentrates on BIO, which is the most straightforward to learn.
BIO Overview
BIO uses the traditional stream model provided by java.io . It includes file and directory operations via File and data transfer through input and output streams. Although simple and intuitive, BIO blocks the thread during read/write operations and is unsuitable for high‑concurrency scenarios.
I/O Streams
Streams are the core concept of Java I/O. An input stream reads data from a source, while an output stream writes data to a destination. Streams can be byte‑oriented or character‑oriented:
Byte streams : InputStream and OutputStream .
Character streams : Reader and Writer .
Byte streams handle raw binary data (e.g., images, audio), whereas character streams are intended for text data. Conversions between them are performed with InputStreamReader and OutputStreamWriter .
Choosing Between Byte and Character Streams
Use character streams for pure text files; for all other file types, byte streams are required. Both stream types provide the same core methods ( read() , write() , flush() , close() ), making their usage patterns similar.
Practical Example – Writing a Text File
The following program demonstrates how to create a file, bind it to a FileOutputStream , convert the byte stream to a character stream with OutputStreamWriter , and write user‑provided content using PrintWriter . It employs the try‑with‑resources construct to automatically close streams.
package com.learnfile;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
public class WriteToFilesAppMain {
private static final Scanner in = new Scanner(System.in);
public static void main(String[] args) throws IOException {
File targetFile = createFile();
writeToFile(targetFile);
System.out.println("程序执行结束");
}
private static void writeToFile(File targetFile) throws IOException {
try (FileOutputStream fos = new FileOutputStream(targetFile);
OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
PrintWriter pw = new PrintWriter(osw)) {
System.out.println("输入的内容会实时写入文件,如果输入空行则结束");
while (true) {
String lineToWrite = in.nextLine().trim();
System.out.println("输入内容为:" + lineToWrite);
if (lineToWrite.isBlank()) {
System.out.println("输入结束");
break;
} else {
pw.println(lineToWrite);
pw.flush();
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
private static File createFile() throws IOException {
System.out.println("请输入文件名:");
String fileName = in.nextLine().trim();
File f = new File("." + File.separator + fileName + ".txt");
if (f.isFile()) {
System.out.println("目标文件存在,删除:" + f.delete());
}
System.out.println(f.createNewFile());
return f;
}
}Practical Example – Reading a Text File
This program reads a file line by line, converts each line to uppercase, and prints it. It uses FileInputStream , InputStreamReader , and BufferedReader , also wrapped in a try‑with‑resources block.
package com.learnfile;
import java.io.*;
import java.nio.charset.StandardCharsets;
public class ReadStringFromFileAppMain {
private static final String SOURCE_FILE_NAME = "file1.txt";
public static void main(String[] args) {
File sourceFile = new File("." + File.separator + SOURCE_FILE_NAME);
ReadLineFromFile(sourceFile);
}
private static void ReadLineFromFile(File sourceFile) {
try (FileInputStream fis = new FileInputStream(sourceFile);
InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(isr)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line.trim().toUpperCase());
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}Summary
The article covered Java I/O streams, distinguishing between byte and character streams, explaining when to use each, and showing how to convert between them. By studying the two complete examples—one for writing and one for reading—you gain a solid foundation for handling file I/O in Java.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.