IO流的定义
Java 的 I/O(输入/输出)流是用于处理输入和输出数据的类库。通过流,程序可以从各种输入源(如文件、网络)读取数据,或将数据写入目标位置(如文件、控制台)。
I/O 流分为两大类:字节流 和 字符流,分别用于处理字节级和字符级的数据:
- 字节流:处理 8 位字节数据,适合于处理二进制文件,如图片、视频等。主要类是
InputStream和OutputStream及其子类。 - 字符流:处理 16 位字符数据,适合于处理文本文件。主要类是
Reader和Writer及其子类。
字符流和字节流的区别
- 字节流一般用来处理图像、视频、音频、PPT、Word等类型的文件。字符流一般用于处理纯文本类型的文件,如TXT文件等,但不能处理图像视频等非文本文件。用一句话说就是:字节流可以处理一切文件,而字符流只能处理纯文本文件。
- 字节流本身没有缓冲区,缓冲字节流相对于字节流,效率提升非常高。而字符流本身就带有缓冲区,缓冲字符流相对于字符流效率提升就不是那么大了。
编码和解码
输出流将缓冲区存储的字符通过查ASCII表转换为对应数字再进行编码
输入流对文件进行解码转换为ASCII码对应的数字,然后通过查表转换为读取到的字符
中文编码通常使用GBK字符集
规则
- 汉字使用两个字节进行存储
- 高位字节以1开头,转换成十进制后为负数
Unicode字符集(万国码)是国家标准字符集同时兼任ASCII码
- UTF-16编码:使用2-4个字节保存
- UTF-32编码:固定4个字节保存
- UTF-8编码:使用1-4个字节保存
- ASCII码:1个字节
- 简体中文:3个字节
乱码的原因
- 字符编码与解码不一致。乱码问题常常由字符编码(比如 UTF-8、GBK)和解码过程的不一致引起。如果在编码时使用了一种字符集,而在解码时使用了另一种,字符将无法正确显示,从而出现乱码。
文件流
一切文件(文本、视频、图片)的数据都是以二进制的形式存储的,传输时也是。所以,字节流可以传输任意类型的文件数据。
文件字节流
FileOutputStream文件输出流
构造方法
public FileOutputStream(String s):接收文件路径创建输出流,如果文件不存在,则创建一个新文件;如果文件已经存在,则覆盖原有文件。public FileOutputStream(File file):使用文件对象创建FileOutputStream对象
写入数据
public void write(int b):写入一个ASCII码为b的字符public void write(char b):写入一个字符public void write(byte[] b):写入一个字节数组public void write(byte[] b,int off,int len):从指定的字节数组写入 len 字节到此输出流,从偏移量 off开始。 也就是说从off个字节数开始一直到len个字节结束
追加数据
在构造方法中加入第二个Boolean类型参数指示是否继续读写
FileInputStream文件输入流
构造方法
FileInputStream(String name):创建一个 FileInputStream 对象,并打开指定名称的文件进行读取。文件名由 name 参数指定。如果文件不存在,将会抛出 FileNotFoundException 异常。FileInputStream(File file):创建一个 FileInputStream 对象,并打开指定的 File 对象表示的文件进行读取。
读入数据
public int read():read()方法会读取一个字节并返回其整数表示。如果已经到达文件的末尾,则返回 -1。如果在读取时发生错误,则会抛出IOException异常。public int read(byte[] b) throws IOException:读取数据并将其存储在字节数组b中。返回实际读取的字节数。public int read(byte[] b, int off, int len) throws IOException:从文件中读取最多len个字节的数据,存储到字节数组b中,从偏移量off开始。返回实际读取的字节数。
常见操作
文件拷贝
|
|
文件夹拷贝
|
|
文件字符流
字符流 = 字节流 + 编码表
FileReader文件输入流
一次读取一个字节,遇到中文时一次读入多个字节
构造方法
FileReader(File file):创建一个新的FileReader,参数为File对象。FileReader(String fileName):创建一个新的FileReader,参数为文件名。FileReader(File file,Charset set):创建一个新的FileReader,参数为File对象和字符集。
读取数据
public int read():每次可以读取一个字符,返回读取的字符(转为 int 类型),当读取到文件末尾时,返回-1public int read(char[]buffer,int off,int len):读取多个字符到字符数组buffer,返回值为读入的字符数,读到文件末尾返回-1
释放资源
public int close():释放字符流
FileWriter文件输出流
构造方法
FileWriter(File file): 创建一个新的 FileWriter,参数为要读取的File对象。可以后跟Boolean参数指定是否追加数据FileWriter(String fileName): 创建一个新的 FileWriter,参数为要读取的文件的名称。可以后跟Boolean参数指定是否追加数据
写入数据
public void write(int b):写入一个ASCII码为b的字符public void write(String b):写入字符串public void write(String str, int off, int len):将指定字符串的一部分写入输出流public void write(char[] cbuf):将指定字符数组写入输出流public void write(char[] cbuf, int off, int len):将指定字符数组的一部分写入输出流
关闭和刷新
flush():刷新缓冲区,将缓冲区中的数据强制写入目标设备或流中,流对象可以继续使用。close():先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。
缓冲流:
缓冲流是对基础流的包装,可以显著提高 I/O 性能。常见的缓冲流有 BufferedInputStream、BufferedOutputStream、BufferedReader 和 BufferedWriter,它们通过内部缓冲区减少实际 I/O 操作的次数。
在处理大文件或频繁 I/O 操作时,使用缓冲流可以有效提高性能。
字节缓冲流
底层自带8KB的缓冲区
BufferedInputStream 字节缓冲输入流
构造方法
BufferedInputStream(InputStream in) :创建一个新的缓冲输入流,注意参数类型为InputStream。
读入数据
public int read():read()方法会读取一个字节并返回其整数表示。如果已经到达文件的末尾,则返回 -1。如果在读取时发生错误,则会抛出IOException异常。public int read(byte[] b) throws IOException:读取数据并将其存储在字节数组b中。返回实际读取的字节数。public int read(byte[] b, int off, int len) throws IOException:从文件中读取最多len个字节的数据,存储到字节数组b中,从偏移量off开始。返回实际读取的字节数。
BufferedOutputStream字节缓冲输出流
构造方法
BufferedOutputStream(OutputStream in) :创建一个新的缓冲输入流,注意参数类型为OutputStream。
写入数据
public void write(int b):写入一个ASCII码为b的字符public void write(String b):写入字符串public void write(String str, int off, int len):将指定字符串的一部分写入输出流public void write(char[] cbuf):将指定字符数组写入输出流public void write(char[] cbuf, int off, int len):将指定字符数组的一部分写入输出流
关闭和刷新
flush():刷新缓冲区,将缓冲区中的数据强制写入目标设备或流中,流对象可以继续使用。close():先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。只用关闭高级流的流对象底层会自动关闭基本流
字符缓冲流
BufferedReader字符缓冲输入流
构造方法
BufferedReader(Reader in):创建一个新的缓冲输入流,注意参数类型为Reader。
读取数据
public int read():每次可以读取一个字符,返回读取的字符(转为 int 类型),当读取到文件末尾时,返回-1public int read(char[]buffer,int off,int len):读取多个字符到字符数组buffer,返回值为读入的字符数,读到文件末尾返回-1String readLine(): 读一行数据,读取到最后返回null
释放资源
public int close():释放字符流
BufferedWriter字符缓冲输出流
构造方法
BufferedWriter(Writer out): 创建一个新的缓冲输出流,注意参数类型为Writer。
写入数据
public void write(int b):写入一个ASCII码为b的字符public void write(String b):写入字符串public void write(String str, int off, int len):将指定字符串的一部分写入输出流public void write(char[] cbuf):将指定字符数组写入输出流public void write(char[] cbuf, int off, int len):将指定字符数组的一部分写入输出流public void newLine():输出换行符
关闭和刷新
flush():刷新缓冲区,将缓冲区中的数据强制写入目标设备或流中,流对象可以继续使用。close():先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。
转换流
将字符流和字节流进行连接,实现互相转换
InputStreamReader :将一个字节输入流转换为一个字符输入流,
OutputStreamWriter :将一个字节输出流转换为一个字符输出流。
它们使用指定的字符集将字节流和字符流之间进行转换。常用的字符集包括 UTF-8、GBK、ISO-8859-1 等。
InputStreamReader
作用
- 将字节流(InputStream)转换为字符流(Reader)
- 同时支持指定的字符集编码方式,从而实现字节流到字符流之间的转换。
构造方法
InputStreamReader(InputStream in): 创建一个使用默认字符集的字符流。InputStreamReader(InputStream in, String charsetName): 创建一个指定字符集的字符流。
常用方法
read():从输入流中读取一个字符的数据。read(char[] cbuf, int off, int len):从输入流中读取 len 个字符的数据到指定的字符数组 cbuf 中,从 off 位置开始存放。ready():返回此流是否已准备好读取。close():关闭输入流。
OutputStreamWriter
作用
- 将字符流(Writer)转换为字节流(OutputStream)
- 同时支持指定的字符集编码方式,从而实现字符流到字节流之间的转换。
构造方法
OutputStreamWriter(OutputStream in): 创建一个使用默认字符集的字节流。OutputStreamWriter(OutputStream in, String charsetName):创建一个指定字符集的字节流。
常用方法
write(int c):向输出流中写入一个字符的数据。write(char[] cbuf, int off, int len):向输出流中写入指定字符数组 cbuf 中的 len 个字符,从 off 位置开始。flush():将缓冲区的数据写入输出流中。close():关闭输出流
示例
|
|
序列化流
序列化
是将对象转换为字节流的过程,这样对象可以通过网络传输、持久化存储或者缓存。Java 提供了 java.io.Serializable 接口来支持序列化,只要类实现了这个接口,就可以将该类的对象进行序列化。
反序列化
是将字节流重新转换为对象的过程,即从存储中读取数据并重新创建对象。
ObjectOutputStream序列化流
构造方法
ObjectOutputStream(OutputStream out)
|
|
写入方法
public final writeObject (Object obj):写入一个对象public void write(int b) throws IOExceptionpublic void write(byte[] b, int off, int len) throws IOException
示例
|
|
ObjectInputStream反序列化流
ObjectInputStream 可以读取 ObjectOutputStream 写入的字节流,并将其反序列化为相应的对象(包含对象的数据、对象的类型和对象中存储的属性等信息)。
构造方法
ObjectInputStream(InputStream in)
读入方法
public Object readObject():读入对象public void read():读一个字节
Serializable序列化接口
定义
|
|
注意事项
static和transient修饰的字段是不会被序列化的- 被反序列化后,
transient字段的值被设为初始值
- 被反序列化后,
- Java底层根据类的内容对实现了
Serializable接口的类计算出类型为long的版本号serialVersionUID,若类的代码发生改变会使版本号改变导致无法反序列化 - Java 虚拟机会把字节流中的
serialVersionUID与被序列化类中的serialVersionUID进行比较,如果相同则可以进行反序列化,否则就会抛出序列化版本不一致的异常 - 通常使用
private,static,final来修饰serialVersionUID
transient瞬态关键字
在实际开发过程中,不需要被序列化的字段,比如说用户的一些敏感信息(如密码、银行卡号等),为了安全起见,不希望在网络操作中传输或者持久化到磁盘文件中,那这些字段就可以加上 transient 关键字。
被 transient 关键字修饰的成员变量在反序列化时会被自动初始化为默认值