Skip to content

I/O 操作

Java I/O(Input/Output,输入/输出)用于处理数据的读取和写入操作,主要位于 java.iojava.nio 包中。I/O 主要分为字节流和字符流,同时支持文件操作、缓冲流、数据流和压缩流等功能。

流概述

流(Stream)是 Java I/O 操作的核心概念,它表示数据的流动方向:

  • 输入流(InputStream/Reader):数据从外部流入 Java 程序(如读取文件、网络数据)

25021702.png

图 输入模式
  • 输出流(OutputStream/Writer):数据从 Java 程序流向外部(如写入文件、发送网络数据)

25021703.png

图 输出模式

根据处理的数据类型,流又分为:

  1. 字节流(处理二进制数据):
  • 输入流:InputStream
  • 输出流:OutputStream
  1. 字符流(处理文本数据):
  • 输入流:Reader
  • 输出流:Writer

流的层次结构如下:

字节流:
├── InputStream(抽象类)
│   ├── FileInputStream
│   ├── BufferedInputStream
│   ├── DataInputStream

├── OutputStream(抽象类)
│   ├── FileOutputStream
│   ├── BufferedOutputStream
│   ├── DataOutputStream

字符流:
├── Reader(抽象类)
│   ├── FileReader
│   ├── BufferedReader

├── Writer(抽象类)
│   ├── FileWriter
│   ├── BufferedWriter

输入/输出流

输入流(InputStream/Reader)

输入流用于从外部读取数据。所有输入流类都继承自 InputStreamReader

InputStream 类是字节输入流的抽象类,是所有字节输入类的父类。该类中所有方法遇到错误时都会抛出 IOException 异常。

下面是 InputStream 类常用的方法:

  • int read():读取单个字节,返回读取的字节数据(0-255),如果已到达流的末尾,则返回 -1。
  • int read(byte[] b):读取多个字节,将读取的数据存储到字节数组 b 中。
  • mark(int readlimit):标记流的当前位置。readlimit 参数告知此输入流在标记位置失效之前允许读取的字节数。
  • reset():将输入指针返回到当前所做标记的位置。
  • skip(long n):跳过和丢弃输入流中数据的 n 个字节,并返回实际跳过的字节数。
  • markSupported():判断此输入流是否支持 mark()reset() 方法,支持则返回 true
  • close():关闭此输入流,并释放所有与该流关联的系统资源。

并不是所有的 InputStream 类的子类都支持 InputStream 中定义的所有方法,如 FileInputStream 不支持 mark()reset() 方法。

25021704.png

图 InputStream 类的层次结构

Java 中的字符是 Unicode 编码,InputStream 类是用来处理字节的,并不适合处理字符。因此需要使用字符流来读取文本文件。Reader 类是字符输入流的抽象类,是所有字符输入类的父类。Reader 类中定义了读取字符的抽象方法,如 read()read(char[] cbuf) 等。

Reader 类常用的方法与 InputStream 类似,如 read()read(char[] cbuf)mark(int readlimit)reset()skip(long n)markSupported()close()

25021706.png

图 Reader 类的层次结构

输出流(OutputStream/Writer)

输出流用于将数据写入外部,所有输出流类都继承 OutputStreamWriter

OutputStream 类是字节输出流的抽象类,是所有字节输出流的父类。在遇到错误时会抛出 IOException 异常。

下面是 OutputStream 类常用的方法:

  • write(int b):将指定的字节写入此输出流。
  • write(byte[] b):将 b.length 个字节从指定的字节数组写入此输出流。
  • write(byte[] b, int off, int len):将 len 个字节从指定的字节数组写入此输出流,从偏移量 off 开始。
  • flush():刷新此输出流,并强制写出所有缓冲的输出字节。
  • close():关闭此输出流,并释放与该流关联的所有系统资源。

25021707.png

图 OutputStream 类的层次结构

Writer 类是字符输出流的抽象类,是所有字符输出类的父类。

25021708.png

图 Writer 类的层次结构

File 类

java.io.File 代表文件或目录,用于文件操作(创建、删除、获取信息等)。

文件的创建与删除

File 类有3种构造方法用于创建文件对象:

  • File(String pathname):根据路径名创建文件对象。
  • File(String parent, String child):根据父路径和子路径创建文件对象。
  • File(File parent, String child):根据父文件对象和子路径创建文件对象。

示例:

java
File file = new File("test.txt");
if (!file.exists()) {
    file.createNewFile(); // 创建文件
}
file.delete(); // 删除文件

获取文件信息

25021709.png

表 File 类常用的方法

示例:

java
File file = new File("test.txt");
System.out.println("文件名:" + file.getName());
System.out.println("文件路径:" + file.getAbsolutePath());
System.out.println("文件大小:" + file.length());

文件输入/输出流

FileInputStream 与 FileOutputStream(字节流)

FileInputStream 读取二进制数据,FileOutputStream 写入二进制数据。

FileInputStream 常用的构造方法如下:

  • FileInputStream(File file):根据 File 对象创建输入流。
  • FileInputStream(String name):根据文件名创建输入流。

创建 FileOutputStream 对象时可以指定不存在的文件名,但此文件不能是一个已被其他程序打开的文件。

示例:复制文件

java
try (FileInputStream fis = new FileInputStream("input.txt");
     FileOutputStream fos = new FileOutputStream("output.txt")) {
    byte[] buffer = new byte[1024];
    int len;
    while ((len = fis.read(buffer)) != -1) {
        fos.write(buffer, 0, len);
    }
} catch (IOException e) {
    e.printStackTrace();
}

FileReader 与 FileWriter(字符流)

由于使用 FileInputStreamFileOutputStream 都只提供了对字节或字节数组的读取方法。而汉字在文件中占用两个字节,如果使用字节流读取,会出现乱码。此时需要采用字符流 FileReaderFileWriter 用于读取和写入文本文件。

FileReader 顺序读取文件,只要不关闭流,每次调用 read() 方法就会顺序读取其余的内容,直到源的末尾或流被关闭。

示例:读取文本文件

java
try (FileReader fr = new FileReader("test.txt")) {
    int ch;
    while ((ch = fr.read()) != -1) {
        System.out.print((char) ch);
    }
} catch (IOException e) {
    e.printStackTrace();
}

示例:写入文本文件

java
try (FileWriter fw = new FileWriter("test.txt")) {
    fw.write("Hello, Java I/O!");
} catch (IOException e) {
    e.printStackTrace();
}

带缓存的输入/输出流

缓存是 I/O 操作的一种性能优化。缓存流为 I/O 流增加了内存缓冲区,提高读写效率。

BufferedInputStream 与 BufferedOutputStream

BufferedInputStream 类可以对所有 InputStream 类的对象进行缓冲区处理,提高读取效率。BufferedInputStream 类的构造方法如下:

  • BufferedInputStream(InputStream in):创建一个新的缓冲输入流,使用默认缓冲区大小(32 字节)。
  • BufferedInputStream(InputStream in, int size):创建一个新的缓冲输入流,指定缓冲区大小。

一个最优的缓冲区大小取决于所在操作系统、可用的内存空间以及机器配置。从构造方法可以看出,BufferedInputStream 类是 InputStream 类的子类,BufferedInputStream 对象位于 InputStream 对象之前。

25021710.png

图 BufferedInputStream 读取文件的过程

使用 BufferedOutputStream 和使用 OutputStream 输出信息完全相同,只是 BufferedOutputStream 中可以调用 flush() 方法,将缓冲区中的数据强制输出到目的地。BufferedOutputStream 类的构造方法如下:

  • BufferedOutputStream(OutputStream out):创建一个新的缓冲输出流。
  • BufferedOutputStream(OutputStream out, int size):创建一个新的缓冲输出流,指定缓冲区大小。

flush() 方法就是用于即使在缓冲区没有满的情况下,也会将缓冲区的内容强制写入外设,习惯上称这个过程为刷新。flush() 方法只对使用缓冲区的 OutputStream 类的子类有效。当调用 close() 方法时,系统在关闭流之前也会将缓冲区里的数据刷新到磁盘中。

示例:使用缓冲流复制文件

java
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("input.txt"));
     BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("output.txt"))) {
    byte[] buffer = new byte[1024];
    int len;
    while ((len = bis.read(buffer)) != -1) {
        bos.write(buffer, 0, len);
    }
} catch (IOException e) {
    e.printStackTrace();
}

BufferedReader 与 BufferedWriter

BufferedReader 类与 BufferedWriter 类分别继承 ReaderWriter 类,提供了缓冲区功能,以行为单位用于读取和写入文本文件。

25021711.png

图 BufferedReader 读取文件的过程

BufferedReader 类常用的方法如下:

  • int read():读取单个字符。
  • String readLine():读取一行文本,返回字符串。如果没有数据可读,则返回 null

BufferedWriter 类常用的方法如下:

  • void write(String str, int off, int len):写入字符串的一部分。在调用时,数据并没有立即被写入输出流,而是先写入缓冲区。如果想立刻将缓冲区的数据写入输出流,需要调用 flush() 方法。
  • void newLine():写入一个行分隔符。
  • void flush():刷新该流的缓冲。

示例:读取文本文件

java
try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    e.printStackTrace();
}

示例:写入文本文件

java
try (BufferedWriter bw = new BufferedWriter(new FileWriter("test.txt"))) {
    bw.write("Hello, Buffered I/O!");
    bw.newLine(); // 写入换行符
} catch (IOException e) {
    e.printStackTrace();
}

数据输入/输出流

数据输入/输出流允许程序以与机器无关的方式从底层输入流中读取基本 Java 数据类型(int、double、boolean等)。

DataInputStream 类与 DataOutputStream 类的构造方法如下:

  • DataInputStream(InputStream in):创建一个新的数据输入流。
  • DataOutputStream(OutputStream out):创建一个新的数据输出流。

DataOutputStream 类提供了如下三种写入字符串的方法:

  • writeBytes(String s):将字符串以字节的形式写入输出流。
  • writeChars(String s):将字符串以字符的形式写入输出流。
  • writeUTF(String s):将字符串以 UTF-8 编码写入输出流。

由于 Java 中字符使用 Unicode 编码,是双字节,writeBytes 方法将字符串中每一个字符的低字节内容写入到目标设备中,而 writeChars 方法将字符串中每一个字符的两个字节内容写入到目标设备中,writeUTF 方法将字符串以 UTF-8 编码后的长度写入到目标设备中,然后才是每一个字节的 UTF 编码。

DataInputStream 类只提供了一个 readUTF() 方法返回字符串。这是因为要在一个连续的字节流中读取一个字符串,如果没有特殊的标记作为一个字符串的结尾,并且不知道这个字符串的长度,就无法知道读取到什么位置才是这个字符串的结束。DataOutputStream 类中只有 writeUTF() 方法??目标设备写入字符串的长度,所以能准确地读取字符串。

示例:

java
try (DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.dat"))) {
    dos.writeInt(100);
    dos.writeDouble(3.14);
} catch (IOException e) {
    e.printStackTrace();
}

try (DataInputStream dis = new DataInputStream(new FileInputStream("data.dat"))) {
    System.out.println(dis.readInt());  // 100
    System.out.println(dis.readDouble()); // 3.14
} catch (IOException e) {
    e.printStackTrace();
}

ZIP 压缩输入/输出流

ZIP 压缩管理文件是一种十分典型的文件压缩形式,使用压缩可以节省存储空间。使用 java.util.zip 包中的 ZipInputStreamZipOutputStream 可以实现 ZIP 文件的解压缩和压缩。

如果要压缩文件,首先需要创建一个 ZipOutputStream 对象,然后调用 putNextEntry() 方法创建一个新的 ZIP 条目,再调用 write() 方法将文件内容写入 ZIP 文件。 如果要解压 ZIP 文件,首先需要创建一个 ZipInputStream 对象,然后调用 getNextEntry() 方法获取 ZIP 条目,再调用 read() 方法读取 ZIP 文件内容。

压缩文件

使用 ZipOutputStream 类可以将文件压缩为 ZIP 文件。

25021712.png

表 ZipOutputStream 类的常用方法
java
try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream("output.zip"));
     FileInputStream fis = new FileInputStream("test.txt")) {
    zos.putNextEntry(new ZipEntry("test.txt"));
    byte[] buffer = new byte[1024];
    int len;
    while ((len = fis.read(buffer)) != -1) {
        zos.write(buffer, 0, len);
    }
    zos.closeEntry();
} catch (IOException e) {
    e.printStackTrace();
}

解压 ZIP 文件

使用 ZipInputStream 类可以将 ZIP 文件解压缩。

25021713.png

表 ZipInputStream 类的常用方法
java
try (ZipInputStream zis = new ZipInputStream(new FileInputStream("output.zip"))) {
    ZipEntry entry;
    while ((entry = zis.getNextEntry()) != null) {
        System.out.println("解压:" + entry.getName());
        zis.closeEntry();
    }
} catch (IOException e) {
    e.printStackTrace();
}
编程洪同学服务平台是一个广泛收集编程相关内容和资源,旨在满足编程爱好者和专业开发人员的需求的网站。无论您是初学者还是经验丰富的开发者,都可以在这里找到有用的信息和资料,我们将助您提升编程技能和知识。
专业开发
高端定制
售后无忧
站内资源均为本站制作或收集于互联网等平台,如有侵权,请第一时间联系本站,敬请谅解!本站资源仅限于学习与参考,严禁用于各种非法活动,否则后果自行负责,本站概不承担!