Java SE-FILE类与IO流
File类的使用
位置:package java.io
构造器:
public File(String pathname)
pathname
可以用绝对路径或相对路径
public File(String parent, String child)
parent
:父路径名字符串,如果为null则等效于上一种构造器child
:子路径名字符串,如果它是绝对路径则直接忽略父路径名- 如果二者都是相对路径,则把
child
添加在parent
的后面
public File(File parent, String child)
方法
见File.java
(快捷键:Ctrl + N
查找类,Ctrl + F12
查找当前文件中的方法)。
例:输出一个目录下的所有文件名(递归)
1 |
|
IO流
IO流就是Java中用来“读”和“写”数据的通道。
- 按数据方向分
- 输入流:读取数据到程序中
- 输出流:写数据到外部设备
- 按数据类型分:
- 字节流:以字节(8bit)为单位,如
InputStream
,OutputStream
- 字符流:以字符(长度不定)为单位,如
Reader
,Writer
- 字节流:以字节(8bit)为单位,如
节点流是直接与 数据源 或 数据目的地 打交道的流,常用于最底层的 I/O 操作。
处理流是“套接”在已有的流(通常是节点流)上,对数据进行 加工处理 的流。
注意:字符流专门用于处理文本内容,字节流用于处理所有类型的数据(包括文本、图片、视频、音频等)。
核心类结构图:
1 |
|
左列:抽象基类
右列:节点流(文件流),直接连接数据源和数据目的地。
FileReader、FileWriter
一般步骤:
- 创建读取或写出的File类实例
- 创建输入流或输出流
- 具体的读或写的过程
- read(char[] cbuffer):int
- write(char[] cbuffer, 0, len):void
- 关闭流资源
注意: 1. 处理异常 2. 对于输入流,File类的实例对应的文件必须已经存在
3.
对于输出流,可以不存在,会自动创建并写入数据到此文件中;如果文件已经存在,则FileWriter(File file, true/false)
FileInputStream、FileOutputStream
- 创建读取或写出的File类对象
- 创建输入流或输出流
- 具体的读入或写出过程,仍然是read和write
- 关闭流资源
缓冲流
4个处理流:
- BufferedInputStream
- BufferedOutputStream
- BufferedReader
- BufferedWriter
缓冲流的作用:提升文件读写的效率。
直接使用FileInputStream
读取文件时,每次 .read()
都会和磁盘打交道,这样效率会非常低。而缓冲流会一次性从底层流中读取大量数据存入内存缓冲区,之后就从内存中读取,提高了效率。
磁盘 I/O 是非常慢的,如果你每次都去磁盘读 1 字节,CPU 会一直等,效率很低。而一次性读取一个 8KB 缓冲区,可以极大减少磁盘读取次数,提高程序运行效率。
1 |
|
BufferedInputStream
原理:
- 他有一个内部的
byte[]
缓冲区,默认大小8192字节 - 当调用
.read()
时,如果缓冲区有数据,就直接从缓冲区读;如果缓冲区空了,就从底层流中读取一大块(比如8192字节)放入缓冲区 - 类似地,
BufferedOutputStream
也是把数据先写入缓冲区,等缓冲区满了或调用.flush()
时才真正写到文件。
从文本文件读取文本到控制台(一次读一行):
1 |
|
转换流
转换流(Conversion Streams)是连接字节流和字符流之间的桥梁,它们的作用是:
将“字节”转换成“字符”,或相反地将“字符”转换成“字节”。
如果能够确认文件的编码格式与你的程序使用的编码一致,那确实可以不显式使用转换流(InputStreamReader / OutputStreamWriter),直接用字符流 FileReader / FileWriter也是没问题的。
类名 | 方向 | 用途 |
---|---|---|
InputStreamReader | 字节 → 字符 | 把字节输入流转换成字符输入流(读文件) |
OutputStreamWriter | 字符 → 字节 | 把字符输出流转换成字节输出流(写文件) |
举一个例子:
1 |
|
注意,OutputStreamWriter
也是带缓冲的流,只有当缓冲区满了或者调用flush()
或者关闭流时,才会向硬盘中写入数据。.write()
方法只是写到了它的缓冲区中,并不一定写到了磁盘中。OutputStreamWriter.close()
方法会自动调用flush()
方法,并且关闭底层的FileOutputStream
。
但是InputStreamReader
本身不带缓冲区,它只是一个“编码解码器”,用来把字节流转换为字符流。BufferedReader
才带有缓冲读取功能。如果要提高读取的效率,应该给转换流外面套上一层缓冲流:
1 |
|
英文字符不会出现乱码问题。
在内存中的字符问题:内存中的长度与编程语言有关,Java的一个char
在内存中占用两个字节。在硬盘中占用多少字节取决于编码方式。
对象流
将内存中定义的变量保存在文件中。
ObjectInputStream
从字节流中中还原出对象
ObjectOutputStream
将对象转换成字节流
- ObjectOutputStream 写入的内容不是纯文本格式,而是包含了Java 序列化头信息 + 字符串内容的二进制数据。
对象的序列化过程
对象的序列化机制(Object Serialization)是指将对象转换成一连串字节的过程,以便:
- 保存到磁盘(例如写入文件);
- 通过网络传输;
- 缓存;
- 后续还原(反序列化)为原始对象。
在内存中的对象不能直接写到硬盘或通过网络发送,必须先转成可传输的格式(字节流),这就是序列化的目的。
反序列化:将字节流还原为对象
- 序列化的对象的类必须实现接口。
- 要求自定义类声明一个全局常量
static final long serialVersionUID = xxxxxxL
用来唯一地标识当前的类;虽然系统会自动生成UID,但是因为容易变化所以不推荐。 - 要求自定义类的各个属性也是可以序列化的
- 基本数据类型默认可序列化
- 引用数据类型也实现了接口Serializable
1 |
|
项 | 说明 |
---|---|
transient 关键字 |
修饰的字段不会被序列化 |
静态字段 | static 修饰的变量不会被序列化(它属于类,不属于实例) |
父类未实现Serializable |
则父类的字段不会被序列化 |
serialVersionUID |
建议手动指定版本号,防止类结构变化导致反序列化失败 |