341. Java I/O API - 理解 Java I/O 主要概念

0 阅读5分钟

341. Java I/O API - 理解 Java I/O 主要概念

介绍 Java I/O API

在Java中,I/O代表输入和输出。Java I/O API应用程序提供了从外部访问数据所需的所有工具。在应用程序的语境中,“外部”通常指两个主要的元素:文件系统(通常是硬盘或内存)和网络。事实上,还可以通过Java I/O及其扩展访问第三个元素:堆外内存。

Java I/O API能够读取和写入文件,使用不同的协议通过网络获取或发送数据。访问数据库的Java数据库连接(JDBC)API就使用了Java I/O API通过TCP/IP访问数据库。许多著名的API都是基于Java I/O API构建的。

理解 Java I/O、Java NIO 和 Java NIO2

Java I/O API是在90年代中期与JDK的早期版本一起创建的。

2002年,Java SE 1.4发布了Java NIO(非阻塞I/O),引入了新的类、概念和功能。NIO的主要特性是非阻塞I/O,能让应用程序在执行I/O操作时不被阻塞。虽然Java NIO发布后,可以考虑重新编写部分I/O代码来利用其改进,但Java I/O的基本模式保持不变。

2011年,Java SE 7发布了Java NIO2,增加了更多的类和新概念,并带来了新的I/O模式。Java SE 8更新了部分NIO2类,进一步提升了Java I/O的能力。

本教程将涵盖Java I/O、Java NIO和Java NIO2的三大部分。

访问文件

Java I/O有两个主要概念:定位资源和打开流。接下来的内容将展示如何通过文件系统访问文件,以及Java I/O API如何组织其流。

访问文件有两种方式:一种使用File类,另一种使用Path接口。

使用 File 类访问文件

File类是在Java SE 1.0中引入的,它代表了访问文件的传统方式。可以将此类视为表示文件路径的字符字符串的包装器。该路径可以是绝对路径或相对路径,并且可以代表常规文件或目录。可以检查文件是否存在,是否可读或可修改,还可以执行创建、复制等操作。

示例:

File file = new File("example.txt");
if (file.exists()) {
    System.out.println("文件存在");
}
使用 Path 接口访问文件

自Java SE 7起,Path接口作为Java NIO2的一部分被引入,用以解决File类的一些缺点。Path接口解决了以下问题:

  • File类中的许多方法在失败时不会抛出异常,导致无法获得有用的错误信息。例如,调用file.delete()如果删除失败,只会返回false,但无法确定失败原因。
  • File类的rename()方法在不同平台上的表现不一致。
  • File类对符号链接的支持较差。
  • 对文件元数据(如文件权限、所有者等)的支持较差。
  • File类中的方法在处理大量数据时可能导致性能问题。

因此,从Java SE 7开始,推荐使用Path接口来替代File类。

示例:

Path path = Paths.get("example.txt");
if (Files.exists(path)) {
    System.out.println("文件存在");
}

理解 I/O 流

I/O流代表一个输入源或输出目标。流可以表示许多不同类型的源和目标,包括磁盘文件、设备、其他程序和内存数组等。

流支持多种数据类型,包括字节、原始数据类型、本地化字符和对象。一些流仅传递数据,而其他流则以有用的方式操作和转换数据。

尽管内部工作方式不同,但所有流都向程序提供相同的简单模型:流是数据的序列。程序使用输入流从源中读取数据,一次读取一个项目;而使用输出流将数据写入目标,一次写入一个项目。

数据源和数据目标可以是任何存储、生成或消耗数据的事物,当然,包括磁盘文件,但源或目标也可以是其他程序、外部设备、网络套接字或数组等。

I/O流和Java 8引入的Stream API是两个不同的概念。尽管它们名称相同,容易引起混淆,但它们的概念是不同的。

Java I/O API定义了两种内容类型:

  • 字符内容:比如文本文件、XML或JSON文档。
  • 字节内容:比如图像或视频文件。

并且定义了两种操作内容的方式:读取和写入。

基于这些,Java I/O API定义了四个基础抽象类,每个类都表示一种特定类型的I/O流和具体操作:

读取写入
字符流:Reader字符流:Writer
字节流:InputStream字节流:OutputStream

所有的字节流都继承自InputStreamOutputStream,这包括许多子类。接下来,将看到一些常见的字节流和字符流类。

示例:读取文件内容

使用字节流读取文件:

InputStream inputStream = new FileInputStream("example.txt");
int data;
while ((data = inputStream.read()) != -1) {
    System.out.print((char) data);
}
inputStream.close();

使用字符流读取文件:

Reader reader = new FileReader("example.txt");
int data;
while ((data = reader.read()) != -1) {
    System.out.print((char) data);
}
reader.close();

总结

通过Java I/O API,可以实现对文件、网络资源、堆外内存等的访问。虽然File类提供了基本的文件操作,但Path接口更为现代,提供了更多的功能和更好的平台兼容性。理解流的工作方式对于编写高效的I/O操作代码至关重要,正确地选择字节流或字符流可以提高性能并避免常见错误。