Java 的旧的 IO 是指在 Java NIO 出现之前,Java 标准库中提供的传统 IO API。旧的 IO API 在 Java NIO 出现之前是主要的 IO 技术,它的优点是简单易用、容易理解,适合于处理少量数据的场景。旧的 IO API 在处理大量数据时效率较低,且不支持非阻塞 IO 操作,在处理大量数据和高并发的场景下效率较低,因此在实际开发中,应该根据具体的需求选择合适的 IO 技术。
字节流(Byte Stream)
字节流(Byte Stream)是 Java IO 操作中处理字节数据的基本流类型,它可以读取和写入字节数据。
字节流主要用于读取和写入二进制数据,如图像、声音、视频等。在使用字节流进行输入输出时,数据以字节为单位进行读取和写入,比字符流更为底层和高效。因此,字节流在处理一些二进制数据的场景下很常用。
字节流有两个抽象基类:InputStream 和 OutputStream。InputStream 和 OutputStream 都是抽象类,不能直接使用。Java 提供了一些常见的子类,用于处理不同的输入输出场景:
下面的例子演示了如何从文件中读取二进制数据并将其写入到另一个文件中:
package cn.leetcode;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class ByteStreamExample {
public static void main(String[] args) {
// 这里的输入输出不一定是文本类型的文件,可以是任意类型的文件
String inputFile = "input.txt";
String outputFile = "output.txt";
try (
// 创建输入流读取 input.txt 文件的内容
FileInputStream inStream = new FileInputStream(inputFile);
// 创建输出流将读取到的内容写入到 output.txt 文件中
FileOutputStream outStream = new FileOutputStream(outputFile)) {
// 读取和写入操作
int bytesRead;
byte[] buffer = new byte[1024];
while ((bytesRead = inStream.read(buffer)) != -1) {
outStream.write(buffer, 0, bytesRead);
}
System.out.println("文件读取并复制完成.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
说明:从Java 7开始,引入了 try-with-resources 语句,使得在使用完毕后自动关闭资源变得更加容易。该语句可以自动关闭在 try 语句块中声明的资源,而无需使用显式的 finally 块或 try-catch 语句。
try-with-resources 语句的语法如下:
try (Resource1 res1 = new Resource1();
Resource2 res2 = new Resource2();
/* ... */) {
// 使用 res1、res2 等资源进行操作
} catch (Exception e) {
// 处理异常
}
在 try 语句块中,我们声明需要使用的资源,这些资源必须是 java.lang.AutoCloseable 接口的实现类或其子类。当程序执行到 try 语句块的结尾时,无论是否发生异常,Java 都会自动调用这些资源的 close() 方法来关闭它们。
字符流
字符流(Character Stream)是 Java IO 操作中处理字符数据的基本流类型,它可以读取和写入字符数据。虽然 Java 提供了字节流来处理二进制数据,但是在处理文本数据时,使用字符流比使用字节流更加方便和高效。
字符流可以将字节转换为字符,以便于对文本进行处理。与字节流不同的是,字符流可以直接读写 Unicode 字符,而不需要自己实现字节到字符的转换。在处理文本文件时,字符流比字节流更加方便,因为它可以正确地处理字符集和换行符等问题。
字符流主要用于读取和写入文本数据,如文本文件、XML、HTML 等。在使用字符流进行输入输出时,数据以字符为单位进行读取和写入,比字节流更为高级和方便。因此,字符流在处理一些文本数据的场景下很常用。
字符流有两个抽象基类:Reader 和 Writer。Reader 和 Writer 都是抽象类,不能直接使用。Java 提供了一些常见的子类,用于处理不同的输入输出场景:
字符流还有一个重要的优点,即在读取和写入文件时,字符流可以通过内部缓冲区实现读写操作的优化。这可以减少对底层文件系统的访问,从而提高程序的性能。
下面的例子演示了如何从文件中读取文本数据并将其写入到另一个文件中:
package cn.leetcode;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CharStreamExample {
public static void main(String[] args) {
String inputFile = "input.txt";
String outputFile = "output.txt";
try (
// 创建输入流读取 input.txt 文件的内容
FileReader inReader = new FileReader(inputFile);
// 创建输出流将读取到的内容写入到 output.txt 文件中
FileWriter outWriter = new FileWriter(outputFile)) {
// 读取和写入操作
int c;
while ((c = inReader.read()) != -1) {
outWriter.write(c);
}
System.out.println("文件读取并复制完成.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
缓冲流(Buffered Stream)
缓冲流(Buffered Stream)是 Java IO 操作中提供的一种包装流,用于提高 IO 的效率。缓冲流可以在读取或写入数据时提高效率,通过内部缓冲区来减少对底层输入输出流的访问次数,从而减少 IO 操作的开销。
缓冲流包含两个基本类:BufferedInputStream 和 BufferedOutputStream,以及两个字符流的缓冲类:BufferedReader 和 BufferedWriter。
缓冲流的作用主要有以下几点:
以下是几个典型的使用例子:
package cn.leetcode;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class BufferWriterExample {
public static void main(String[] args) {
try (BufferedWriter writer = new BufferedWriter(new FileWriter("input.txt"))) {
// 「力扣」第 1 题英文题面
writer.write("Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target.\n");
writer.newLine();
writer.write("You may assume that each input would have exactly one solution, and you may not use the same element twice.\n");
writer.newLine();
writer.write("You can return the answer in any order.");
} catch (IOException e) {
e.printStackTrace();
}
}
}
package cn.leetcode;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class BufferReaderExample {
public static void main(String[] args) {
try (BufferedReader reader = new BufferedReader(new FileReader("input.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
对象流(Object Stream)
对象流(Object Stream)是 Java IO 操作中用于序列化和反序列化 Java 对象的一种流类型,它可以将 Java 对象写入到流中或从流中读取 Java 对象。
Java 中提供了两个对象流类:ObjectInputStream 和 ObjectOutputStream。ObjectInputStream 和 ObjectOutputStream 都是处理二进制数据的流,它们支持将 Java 对象序列化为字节流,或将字节流反序列化为 Java 对象。在进行对象序列化和反序列化时,需要实现 java.io.Serializable 接口,这个接口没有任何方法,只是一个标记接口,表示这个类可以被序列化。
对象流的作用主要有以下几点:
下面是使用对象流的例子:
package cn.leetcode.objectstreamdemo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class LeetCoder implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private Integer age;
private Double salary;
}
package cn.leetcode.objectstreamdemo;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class SerializeDemo {
public static void main(String[] args) {
LeetCoder leetCoder = new LeetCoder();
leetCoder.setName("LeetCoder1");
leetCoder.setAge(22);
leetCoder.setSalary(10000.0);
try (FileOutputStream fileOut = new FileOutputStream("LeetCoder.ser"); ObjectOutputStream out = new ObjectOutputStream(fileOut)) {
out.writeObject(leetCoder);
out.close();
fileOut.close();
System.out.println("Serialized data is saved in LeetCoder.ser");
} catch (IOException i) {
i.printStackTrace();
}
}
}
这个例子中,我们创建了一个 LeetCoder 对象,并使用对象输出流将它序列化到文件中。
package cn.leetcode.objectstreamdemo;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class DeserializeDemo {
public static void main(String[] args) {
LeetCoder leetCoder;
try (FileInputStream fileIn = new FileInputStream("LeetCoder.ser"); ObjectInputStream in = new ObjectInputStream(fileIn)) {
leetCoder = (LeetCoder) in.readObject();
} catch (IOException i) {
i.printStackTrace();
return;
} catch (ClassNotFoundException c) {
System.out.println("LeetCoder class not found");
c.printStackTrace();
return;
}
System.out.println("Deserialized LeetCoder...");
System.out.println("leetCoder = " + leetCoder);
}
}
这个例子中,我们从文件中读取序列化后的 LeetCoder 对象,并使用对象输入流将它反序列化成一个新的 LeetCoder 对象。最后,我们打印出这个新的 LeetCoder 对象的属性值。
NIO.2
值得注意的是,Java 在 JDK 7 之后对旧 IO 进行了优化,称为 NIO.2,新增了许多新的类和功能。因此,现在的 Java 开发人员可以选择使用新 IO 或者 NIO.2,也可以使用旧 IO,根据具体的场景选择使用合适的 I/O 库。
以下是 NIO.2 相对于旧 IO 的一些变化:
示例代码:
package cn.leetcode;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class DirectoryStreamExample {
public static void main(String[] args) throws IOException {
Path dir = Paths.get("C:/Users/username/Documents");
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
for (Path file : stream) {
System.out.println(file.getFileName());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
在上述示例代码中,首先创建一个Path对象来表示需要枚举的目录,这里是用户文档目录。然后,使用 Files 类的 newDirectoryStream() 方法创建一个 DirectoryStream<Path> 对象。这个方法的参数是需要枚举的目录,返回一个包含目录中所有文件的 DirectoryStream<Path> 对象。
接着,使用 try-with-resources 语句打开一个 try 块,该语句会在使用完 DirectoryStream 对象后自动关闭流。然后,使用 for-each 循环遍历 DirectoryStream 对象,对于每个文件,使用 getFileName() 方法获取文件名,并将其输出到控制台。
需要注意的是,在使用 DirectoryStream 对象时,必须使用 try-with-resources 语句或者手动关闭流,以便在使用完对象后释放资源。
此外,DirectoryStream 还支持使用过滤器过滤目录中的文件。可以使用 Files 类的 newDirectoryStream() 方法的重载版本来指定过滤器。例如:
Path dir = Paths.get("C:/Users/username/Documents");
DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.txt");
总之,Java NIO.2 扩展了Java I/O 的功能,使得 Java 应用程序可以更方便地访问文件系统和网络资源,提高了系统的性能和可扩展性。
注意:NIO.2 是 Java SE 7 引入的一组新的 I/O API,而 NIO 是 Java SE 1.4 中引入的非阻塞I/O(即 New I/O)API,是我们下一节要和大家介绍的内容。
阅读量:2028
点赞量:0
收藏量:0