2528 字
13 分钟

Java IO操作详解

2026-02-03
浏览量 加载中...

Java IO操作详解#

什么是IO操作?#

IO(Input/Output)操作是指程序与外部设备之间的数据传输。在Java中,IO操作主要包括文件读写、网络通信、标准输入输出等。

IO流的分类#

Java中的IO流可以按照不同的标准进行分类:

1. 按数据流向分类#

  • 输入流(Input Stream):从外部设备读取数据到程序中
  • 输出流(Output Stream):从程序中写入数据到外部设备

2. 按数据类型分类#

  • 字节流(Byte Stream):以字节为单位处理数据,适用于所有类型的数据
  • 字符流(Character Stream):以字符为单位处理数据,适用于文本数据

3. 按流的功能分类#

  • 节点流(Node Stream):直接与数据源或目标相连的流
  • 处理流(Processing Stream):对节点流进行包装,提供额外的功能

IO流的层次结构#

字节流#

输入字节流#

  • InputStream:所有输入字节流的父类
    • FileInputStream:从文件中读取字节
    • ByteArrayInputStream:从字节数组中读取字节
    • BufferedInputStream:缓冲输入流,提高读取效率
    • DataInputStream:数据输入流,用于读取基本数据类型

输出字节流#

  • OutputStream:所有输出字节流的父类
    • FileOutputStream:向文件中写入字节
    • ByteArrayOutputStream:向字节数组中写入字节
    • BufferedOutputStream:缓冲输出流,提高写入效率
    • DataOutputStream:数据输出流,用于写入基本数据类型

字符流#

输入字符流#

  • Reader:所有输入字符流的父类
    • FileReader:从文件中读取字符
    • CharArrayReader:从字符数组中读取字符
    • BufferedReader:缓冲输入流,提高读取效率,支持按行读取
    • InputStreamReader:字节流到字符流的桥梁

输出字符流#

  • Writer:所有输出字符流的父类
    • FileWriter:向文件中写入字符
    • CharArrayWriter:向字符数组中写入字符
    • BufferedWriter:缓冲输出流,提高写入效率
    • OutputStreamWriter:字符流到字节流的桥梁

字节流的使用#

FileInputStream和FileOutputStream#

读取文件#

// 读取文件
try (FileInputStream fis = new FileInputStream("file.txt")) {
int data;
while ((data = fis.read()) != -1) {
System.out.print((char) data);
}
} catch (IOException e) {
e.printStackTrace();
}

写入文件#

// 写入文件
try (FileOutputStream fos = new FileOutputStream("output.txt")) {
String content = "Hello, Java IO!";
byte[] bytes = content.getBytes();
fos.write(bytes);
} catch (IOException e) {
e.printStackTrace();
}

BufferedInputStream和BufferedOutputStream#

使用缓冲流可以提高IO操作的效率:

// 使用缓冲流读取文件
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("file.txt"))) {
int data;
while ((data = bis.read()) != -1) {
System.out.print((char) data);
}
} catch (IOException e) {
e.printStackTrace();
}
// 使用缓冲流写入文件
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("output.txt"))) {
String content = "Hello, Buffered IO!";
byte[] bytes = content.getBytes();
bos.write(bytes);
} catch (IOException e) {
e.printStackTrace();
}

字符流的使用#

FileReader和FileWriter#

读取文件#

// 读取文件
try (FileReader fr = new FileReader("file.txt")) {
int data;
while ((data = fr.read()) != -1) {
System.out.print((char) data);
}
} catch (IOException e) {
e.printStackTrace();
}

写入文件#

// 写入文件
try (FileWriter fw = new FileWriter("output.txt")) {
String content = "Hello, Java IO!";
fw.write(content);
} catch (IOException e) {
e.printStackTrace();
}

BufferedReader和BufferedWriter#

使用缓冲字符流可以提高IO操作的效率,并且BufferedReader支持按行读取:

// 使用BufferedReader读取文件
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
// 使用BufferedWriter写入文件
try (BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"))) {
String content = "Hello, Buffered IO!";
bw.write(content);
bw.newLine(); // 写入换行符
bw.write("Another line");
} catch (IOException e) {
e.printStackTrace();
}

转换流#

InputStreamReader和OutputStreamWriter#

转换流用于在字节流和字符流之间进行转换:

// 使用转换流读取文件(指定编码)
try (InputStreamReader isr = new InputStreamReader(new FileInputStream("file.txt"), "UTF-8")) {
int data;
while ((data = isr.read()) != -1) {
System.out.print((char) data);
}
} catch (IOException e) {
e.printStackTrace();
}
// 使用转换流写入文件(指定编码)
try (OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("output.txt"), "UTF-8")) {
String content = "Hello, 转换流!";
osw.write(content);
} catch (IOException e) {
e.printStackTrace();
}

数据流#

DataInputStream和DataOutputStream#

数据流用于读写基本数据类型:

// 使用DataOutputStream写入基本数据类型
try (DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.dat"))) {
dos.writeInt(100);
dos.writeDouble(3.14);
dos.writeBoolean(true);
dos.writeUTF("Hello");
} catch (IOException e) {
e.printStackTrace();
}
// 使用DataInputStream读取基本数据类型
try (DataInputStream dis = new DataInputStream(new FileInputStream("data.dat"))) {
int intValue = dis.readInt();
double doubleValue = dis.readDouble();
boolean booleanValue = dis.readBoolean();
String stringValue = dis.readUTF();
System.out.println("int: " + intValue);
System.out.println("double: " + doubleValue);
System.out.println("boolean: " + booleanValue);
System.out.println("string: " + stringValue);
} catch (IOException e) {
e.printStackTrace();
}

标准输入输出流#

Java提供了三个标准流:

  • System.in:标准输入流,默认从键盘读取
  • System.out:标准输出流,默认输出到控制台
  • System.err:标准错误流,默认输出到控制台
// 从标准输入读取数据
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
System.out.print("请输入你的名字:");
String name = br.readLine();
System.out.println("你好," + name + "!");
} catch (IOException e) {
e.printStackTrace();
}
// 向标准输出写入数据
System.out.println("这是标准输出");
// 向标准错误写入数据
System.err.println("这是标准错误");

文件操作#

File类#

File类用于表示文件或目录的路径:

// 创建File对象
File file = new File("file.txt");
// 检查文件是否存在
if (file.exists()) {
System.out.println("文件存在");
// 检查是否是文件
if (file.isFile()) {
System.out.println("是文件");
System.out.println("文件名:" + file.getName());
System.out.println("文件路径:" + file.getPath());
System.out.println("绝对路径:" + file.getAbsolutePath());
System.out.println("文件大小:" + file.length() + " 字节");
}
// 检查是否是目录
if (file.isDirectory()) {
System.out.println("是目录");
System.out.println("目录中的文件:");
String[] files = file.list();
for (String f : files) {
System.out.println(f);
}
}
} else {
System.out.println("文件不存在");
// 创建文件
try {
boolean created = file.createNewFile();
if (created) {
System.out.println("文件创建成功");
} else {
System.out.println("文件创建失败");
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 删除文件
boolean deleted = file.delete();
if (deleted) {
System.out.println("文件删除成功");
} else {
System.out.println("文件删除失败");
}

目录操作#

// 创建目录
File directory = new File("test");
boolean created = directory.mkdir();
if (created) {
System.out.println("目录创建成功");
} else {
System.out.println("目录创建失败");
}
// 创建多级目录
File multiDirectory = new File("test1/test2/test3");
boolean multiCreated = multiDirectory.mkdirs();
if (multiCreated) {
System.out.println("多级目录创建成功");
} else {
System.out.println("多级目录创建失败");
}
// 遍历目录
public static void listFiles(File directory) {
if (directory.isDirectory()) {
File[] files = directory.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
System.out.println("目录:" + file.getPath());
listFiles(file); // 递归遍历
} else {
System.out.println("文件:" + file.getPath());
}
}
}
}
}

NIO(New IO)#

Java 4引入了NIO,Java 7引入了NIO.2,它们提供了更高效的IO操作方式:

NIO的核心组件#

  • Channel(通道):双向通道,可以同时进行读写操作
  • Buffer(缓冲区):用于存储数据
  • Selector(选择器):用于多路复用

NIO.2的特性#

  • Path:表示文件路径
  • Files:提供文件操作的静态方法
  • FileVisitor:用于遍历文件树
  • WatchService:用于监控文件变化

NIO.2的使用示例#

// 使用Path和Files读取文件
try {
Path path = Paths.get("file.txt");
byte[] bytes = Files.readAllBytes(path);
String content = new String(bytes, StandardCharsets.UTF_8);
System.out.println(content);
} catch (IOException e) {
e.printStackTrace();
}
// 使用Path和Files写入文件
try {
Path path = Paths.get("output.txt");
String content = "Hello, NIO.2!";
Files.write(path, content.getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
e.printStackTrace();
}
// 遍历文件树
try {
Path startPath = Paths.get(".");
Files.walkFileTree(startPath, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
System.out.println("文件:" + file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
System.out.println("目录:" + dir);
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
e.printStackTrace();
}

最佳实践#

  1. 使用try-with-resources语句:Java 7+推荐使用try-with-resources语句,它会自动关闭资源。

  2. 选择合适的流

    • 处理文本数据时,使用字符流
    • 处理二进制数据时,使用字节流
    • 需要提高效率时,使用缓冲流
  3. 指定字符编码:处理文本数据时,应该显式指定字符编码,避免平台相关的编码问题。

  4. 关闭资源:使用完流后,应该关闭资源,释放系统资源。

  5. 异常处理:IO操作可能会抛出IOException,应该适当处理这些异常。

  6. 使用NIO.2:对于简单的文件操作,推荐使用NIO.2的Files类,它提供了更简洁的API。

  7. 避免频繁的IO操作:频繁的IO操作会降低性能,应该尽量减少IO操作的次数。

  8. 合理使用缓冲区:使用缓冲区可以提高IO操作的效率,但缓冲区大小应该适当。

常见陷阱#

  1. 忘记关闭资源:使用完流后,忘记关闭资源,导致资源泄漏。

  2. 字符编码问题:处理文本数据时,没有指定字符编码,导致乱码。

  3. 缓冲区刷新问题:使用缓冲流时,没有调用flush()方法,导致数据没有及时写入。

  4. 路径分隔符问题:在不同操作系统中,路径分隔符不同,硬编码路径分隔符会导致跨平台问题。

  5. 文件权限问题:没有足够的权限读取或写入文件,导致操作失败。

  6. 文件路径问题:使用相对路径时,当前工作目录可能不是预期的目录,导致文件找不到。

  7. 大文件处理问题:处理大文件时,一次性读取全部内容会导致内存溢出。

  8. 并发访问问题:多个线程同时访问同一个文件,可能会导致数据不一致。

总结#

IO操作是Java编程中的重要部分,它涉及到程序与外部世界的数据交换。Java提供了丰富的IO流类,用于处理不同类型的IO操作。

本文介绍了Java IO流的分类、层次结构和使用方法,以及文件操作和NIO的基本概念。希望本文能够帮助你更好地理解和使用Java的IO操作。

练习#

  1. 编写一个程序,使用字节流读取一个图片文件,然后写入到另一个文件中。

  2. 编写一个程序,使用字符流读取一个文本文件,统计文件中的单词数。

  3. 编写一个程序,使用缓冲流读取一个大文件,提高读取效率。

  4. 编写一个程序,使用DataInputStream和DataOutputStream读写基本数据类型。

  5. 编写一个程序,使用File类创建目录和文件,然后删除它们。

  6. 编写一个程序,使用NIO.2的Files类读取和写入文件。

  7. 编写一个程序,递归遍历一个目录下的所有文件和子目录。

  8. 编写一个程序,从标准输入读取数据,然后输出到标准输出。

通过这些练习,你将更加熟悉Java的IO操作,为后续的学习做好准备。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!

赞助
Java IO操作详解
https://blog.vanilla.net.cn/posts/2026-02-05-java-io-operations/
作者
鹁鸪
发布于
2026-02-03
许可协议
CC BY-NC-SA 4.0

评论区

目录