Dawn's Blogs

分享技术 记录成长

0%

Java高级 (8) IO流

File 类

java.io.File 类:文件和文件目录路径的抽象表示形式,与平台无关

File 能新建、删除、重命名文件和目录,但 File 不能访问文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流

Java File 类提供一个常量,表示路径分隔符:separator。

IO 流

Java程序中,对于数据的输入/输出(IO)操作以流(stream)的方式进行。

  • 输入 input:读取外部数据(磁盘等)到程序(内存)中。
  • 输出 output:将程序(内存)数据输出到磁盘等外部设备中。

流的分类:

  • 按操作数据单位不同分为:字节流(8 bit)、字符流(16 bit)。
  • 按数据流的流向不同分为:输入流、输出流。
  • 按流的角色的不同分为:节点流、处理流。
    • 节点流:直接从数据源或目的地读写数据。
    • 处理流:不直接连接到数据源或目的地,而是“连接”在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能。

image-20230202221611516

image-20230203150444433

节点流(文件流)

文件流用于读取和写入文件,Java 提供 FileInputStream、FileOutputStream、FileReader 和 FileWriter 四种文件流。

处理流

缓冲流

为了提高数据读写的速度,Java API 提供了带缓冲功能的流类,在使用这些流类时,会创建一个内部缓冲区数组,缺省使用 8192 个字节(8KB)的缓冲区

image-20230202222243011

缓冲流要套接在相应的节点流之上,根据数据操作单位可以把缓冲流分为:

  • BufferedInputStream 和 BufferedOutputStream
  • BufferedReader 和 BufferedWriter

使用BufferedInputStream 读取字节文件时,BufferedInputStream会一次性从文件中读取 8192 个字节,存在缓冲区中。直到缓冲区装满了,才重新从文件中读取下一个 8192 个字节数组。

向流中写入字节时,不会直接写到文件,先写到缓冲区中直到缓冲区写满,BufferedOutputStream 才会把缓冲区中的数据一次性写到文件里。使用方法 flush() 可以强制将缓冲区的内容全部写入输出流

关闭流的顺序和打开流的顺序相反。只要关闭最外层流即可,关闭最外层流也会相应关闭内层节点流。close 方法不但会关闭流,而且会在关闭流之前刷新缓冲区。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
BufferedReader br = null;
BufferedWriter bw = null;
try {
// 创建缓冲流对象:它是处理流,是对节点流的包装
br = new BufferedReader(new FileReader("d:\\IOTest\\source.txt"));
bw = new BufferedWriter(new FileWriter("d:\\IOTest\\dest.txt"));
String str;
while ((str = br.readLine()) != null) { // 一次读取字符文本文件的一行字符
bw.write(str); // 一次写入一行字符串
bw.newLine(); // 写入行分隔符
}
bw.flush(); // 刷新缓冲区
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭IO流对象
try {
if (bw != null) {
bw.close(); // 关闭过滤流时,会自动关闭它所包装的底层节点流
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (br != null) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}

转换流

转换流提供了在字节流和字符流之间的转换,Java API 提供了两个转换流:

  • InputStreamReader:将 InputStream 转换为 Reader(解码)。

    • 构造器:public InputStreamReader(InputStream in)public InputSreamReader(InputStream in,String charsetName)
  • OutputStreamWriter:将 Writer 转换为 OutputStream(编码)。

    • 构造器:public OutputStreamWriter(OutputStream out)public OutputSreamWriter(OutputStream out,String charsetName)

字节流中的数据都是字符时,转成字符流操作更高效。

很多时候使用转换流来处理文件乱码问题,实现编码和解码的功能。

image-20230203112609139

对象流

用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可以把 Java 中的对象写入到数据源中,也能把对象从数据源中还原回来

  • 序列化:用 ObjectOutputStream 类保存基本类型数据或对象的机制。
  • 反序列化:用 ObjectInputStream 类读取基本类型数据或对象的机制。

ObjectOutputStream 和 ObjectInputStream 不能序列化 statictransient 修饰的成员变量。

对象序列化

序列化的好处在于可将任何实现了 Serializable 接口的对象转化为字节数据,使其在保存和传输时可被还原。序列化的要求:

  • 实现了 Serializable 接口。
  • 类内部定义了 serialVersionUID 常量。

凡是实现 Serializable 接口的类都有一个表示序列化版本标识符的静态变量:private static final long serialVersionUID;

如果类没有显示定义这个静态常量,其值是 Java 运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID 可能发生变化(建议显式的声明)。

Java 的序列化机制是通过在运行时判断类的 serialVersionUID 来验证版本一致性的。在进行反序列化时,JVM 会把传来的字节流中的 serialVersionUID 与本地相应实体类的 serialVersionUID 进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常(InvalidCastException)。

随机读取文件流

RandomAccessFile 声明在 java.io 包下,但直接继承于 java.lang.Object 类。并且它实现了 DataInput、DataOutput 这两个接口,也就意味着这个类既可以读也可以写。

RandomAccessFile 类支持随机访问的方式,程序可以直接跳到文件的任意地方来读、写文件

  • 支持只访问文件的部分内容
  • 可以向已存在的文件后追加内容

RandomAccessFile 对象包含一个记录指针,用以标示当前读写处的位置。RandomAccessFile 类对象可以自由移动记录指针

  • long getFilePointer():获取文件记录指针的当前位置。
  • void seek(long pos):将文件记录指针定位到 pos 位置。

RandomAccessFile 类的构造器:

  • **public RandomAccessFile(File file, String mode)**。
  • **public RandomAccessFile(String name, String mode)**。

其中,mode 指定文件的访问模式:

  • r:只读。
  • rw:读取和写入。
  • rwd:读取和写入,同步文件内容的更新(立即写入到磁盘中)。
  • rws:读取和写入,同步文件内容和元数据的更新。