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 |
所有的字节流都继承自InputStream和OutputStream,这包括许多子类。接下来,将看到一些常见的字节流和字符流类。
示例:读取文件内容
使用字节流读取文件:
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操作代码至关重要,正确地选择字节流或字符流可以提高性能并避免常见错误。