Java IO流入门:字节流与字符流区别及核心操作示例
Java I/O(Input/Output)是进行设备间数据传输的核心机制。根据数据类型和处理效率的不同,Java提供了字节流(如InputStream、OutputStream)和字符流(如Reader、Writer)两大类。本文将详细解析两者的区别,并通过具体的代码示例,演示如何使用这些流对象完成文件的创建、内容读取、文件复制以及利用缓冲区和工具类提高I/O操作效率。
字节流与字符流的基础概念
Java所有的I/O操作均封装在java.io包中。理解I/O的关键在于掌握分类逻辑:
- 按流向划分:输入流(用于读取数据,基类为
InputStream和Reader)与输出流(用于写入数据,基类为OutputStream和Writer)。 - 按操作数据单位划分:
- 字节流:以字节(8 bit)为单位进行操作,适用于处理二进制文件(如图片、音频)或任何数据类型的通用传输。
- 字符流:以字符(通常是16 bit Unicode)为单位进行操作,专门用于处理纯文本数据。字符流内部通常包含编码转换逻辑,能更好地兼容不同平台的文字标准。
Java继承体系遵循严格的命名规范:子类名称通常由父类名作为后缀。例如,操作文件的字节输入流为FileInputStream,字符输入流为FileReader。
字符流的基本操作
在处理纯文本时,推荐使用字符流。以下示例展示了如何使用FileWriter和FileReader进行基础的文本写入与读取。
第一步:使用 FileWriter 写入文本
目的:将指定字符串内容写入到目标文件中。若文件不存在则自动创建;若已存在且未设置追加模式,原有内容将被覆盖。注意,写入后必须调用flush()刷新缓冲区,或在关闭流时自动刷出。
package javase.day18;
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterDemo {
public static void main(String[] args) {
FileWriter fw = null;
try {
// 创建 FileWriter 对象,指定路径 "C:\\java_test\\FileWriterTest.txt"
// 若目录不存在需提前创建,否则可能抛出异常
fw = new FileWriter("C:\\java_test\\FileWriterTest.txt");
// 将字符串写入流缓冲区
fw.write("alex test23");
// 强制将缓冲区数据写入磁盘文件
fw.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭资源,关闭前会自动执行一次 flush
try {
if (fw != null) {
fw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
第二步:使用 FileReader 读取文本
目的:从文件中读取字符数据并打印到控制台。通常建议使用字符数组作为缓冲区以提高读取效率,避免频繁的系统调用。
package javase.day18;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderTest {
public static void main(String[] args) {
try {
// 读取源文件 "C:\\java_test\\SystemDemo.java"
FileReader fr = new FileReader("C:\\java_test\\SystemDemo.java");
char[] buf = new char[1024]; // 定义缓冲区
int num = 0;
// 循环读取,返回 -1 表示读完
while ((num = fr.read(buf)) != -1) {
// 将读取到的有效字符转换为字符串输出
System.out.println(new String(buf, 0, num));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
文件复制的两种策略
文件复制本质上是“读取源文件”+“写入目标文件”。根据读取粒度的不同,可分为逐字符处理和块处理。块处理(使用数组缓冲)性能通常远优于逐字符处理。
第一种方式:逐字符复制(效率较低)
每次读取一个整数(对应一个char),立即写入。虽然代码简单,但频繁的方法调用会导致较大的性能开销。
第二种方式:数组缓冲复制(推荐)
一次性读取固定大小的字符数组,然后写入对应数量的字符。这种方式大幅减少了系统交互次数。
package javase.day18;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CopyText {
public static void main(String[] args) {
try {
copy_2(); // 建议使用缓冲方式
} catch (IOException e) {
e.printStackTrace();
}
}
// 方式1:逐个字符读写
public static void copy_1() throws IOException {
FileWriter fw = new FileWriter("C:\\java_test\\Copy_SystemDemo.java");
FileReader fr = new FileReader("C:\\java_test\\SystemDemo.java");
int num = 0;
// 每次只读一个字符
while ((num = fr.read()) != -1) {
fw.write(num);
}
fw.close();
fr.close();
}
// 方式2:缓存数组读写
public static void copy_2() throws IOException {
FileWriter fw = new FileWriter("C:\\java_test\\Copy_SystemDemo.java");
FileReader fr = new FileReader("C:\\java_test\\SystemDemo.java");
char[] buf = new char[1024];
int num = 0;
// 批量读取并存入buf
while ((num = fr.read(buf)) != -1) {
// 只写入实际读取到的有效部分
fw.write(buf, 0, num);
}
fw.close();
fr.close();
}
}
使用 BufferedReader/BufferedWriter 提高效率
为了进一步提升文本处理的便利性,Java提供了缓冲字符流:BufferedReader 和 BufferedWriter。它们包裹在底层节点流之外,不仅能提高读写速度,还提供了更便捷的方法,如readLine()可以直接读取一整行文本,无需手动处理换行符。
IO 操作通用选择规律
面对复杂的I/O需求,可遵循以下步骤确定具体的类:
- 明确源和目的:
- 源:输入流 (
InputStream,Reader) - 目的:输出流 (
OutputStream,Writer)
- 源:输入流 (
- 判断数据性质:
- 是否为纯文本?是 → 选择字符流(
Reader/Writer系列);否 → 选择字节流(InputStream/OutputStream系列)。
- 是否为纯文本?是 → 选择字符流(
- 确定具体设备:
- 源设备:硬盘(
File...)、内存(字节数组)、键盘(System.in)。 - 目的设备:硬盘(
File...)、控制台(System.out)。
- 源设备:硬盘(
- 优化需求:
- 是否需要高速读写?若是,在外层包裹
Buffered...类。
- 是否需要高速读写?若是,在外层包裹
实用的 IO 工具类实现
在实际开发中,经常需要将整个文件内容读取为字符串,或者将文本写入文件。以下是一个简单的工具类IOUtil,整合了常用的文件读写功能。
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
public class IOUtil {
/**
* 将文件读入到一个String
* 利用 FileReader + BufferedReader(readLine)
*/
public static String fileReaderStringHandle(String fileName) {
StringBuilder sb = new StringBuilder();
try (
// 使用 try-with-resources 自动管理资源
BufferedReader in = new BufferedReader(new FileReader(new File(fileName).getAbsoluteFile()))
) {
String line;
while ((line = in.readLine()) != null) {
sb.append(line);
sb.append("\n");
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return sb.toString();
}
/**
* 将文件读入到一个 byte[]
* 利用 FileInputStream + BufferedInputStream
*/
public static byte[] fileReaderByteHandle(String fileName) {
try (
BufferedInputStream bf = new BufferedInputStream(new FileInputStream(fileName))
) {
byte[] data = new byte[bf.available()];
bf.read(data);
return data;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 将指定的 text 写入到文件中
* 使用 PrintWriter 方便格式化输出
*/
public static void fileWriterHandle(String fileName, String text) {
try (
PrintWriter out = new PrintWriter(new File(fileName).getAbsoluteFile())
) {
out.print(text);
} catch (Exception e) { // PrintWriter 构造器不抛 IOException,但其他方法可能抛
// 实际生产中建议更细致的异常处理
throw new RuntimeException(e);
}
}
public static void main(String[] args) throws IOException {
// 测试字符串读取
System.out.print(fileReaderStringHandle("src/IOUtil.java"));
// 测试字节读取
for (byte b : fileReaderByteHandle("src/IOUtil.java")) {
System.out.print(b);
}
// 测试文件写入
fileWriterHandle("zj.txt", fileReaderStringHandle("src/IOUtil.java"));
}
}
以上就是Java IO流中字节流与字符流操作的详细内容,更多关于Java文件读写与流处理的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













