学习 Java IO, 不得不提到的就是 JavaIO 流.
流是一组有顺序的, 有起点和终点的字节集合, 是对数据传输的总称或抽象. 即数据在两设备间的传输称为流, 流的本质是数据传输, 根据数据传输特性将流抽象为各种类, 方便更直观的进行数据操作.
IO 流的分类
根据处理数据类型的不同分为: 字符流和字节流
根据数据流向不同分为: 输入流和输出流
Java 流操作有关的类或接口:
字符流和字节流
字符流的由来: 因为数据编码的不同, 而有了对字符进行高效操作的流对象. 本质其实就是基于字节流读取时, 去查了指定的码表. 字节流和字符流的区别:
(1)读写单位不同: 字节流以字节 (8bit) 为单位, 字符流以字符为单位, 根据码表映射字符, 一次可能读多个字节.
(2)处理对象不同: 字节流能处理所有类型的数据(如图片, avi 等), 而字符流只能处理字符类型的数据.
(3)字节流在操作的时候本身是不会用到缓冲区的, 是文件本身的直接操作的; 而字符流在操作的时候下后是会用到缓冲区的, 是通过缓冲区来操作文件, 我们将在下面验证这一点.
结论: 优先选用字节流. 首先因为硬盘上的所有文件都是以字节的形式进行传输或者保存的, 包括图片等内容. 但是字符只是在内存中才会形成的, 所以在开发中, 字节流使用广泛.
输入流和输出流
对输入流只能进行读操作, 对输出流只能进行写操作, 程序中需要根据待传输数据的不同特性而使用不同的流.
1. 缓冲流
缓冲字节流
- BufferedInputStream
- BufferedOutputStream
缓冲字符流
- BufferedReader
- BufferedWriter
处理流内部包含了节点流, 节点流决定了与其沟通的外部设备, 而处理流则增加了其功能.
缓冲流的好处:
缓冲流内部包含一个缓冲区域, 默认 8kb, 每一次程序调用 read 方法其实都是从缓冲区域当中读取内容, 如果读取失败
就说明缓冲区域当中没有内容, 那么就从数据源当中读取内容, 然后会尽可能读取更多的字节放入到缓冲区域当中,
最后缓冲区域当中的内容, 会全部返回给程序.
从缓冲区读取数据会比直接从数据源读取数据的速度快, 效率也更高, 性能更好.
简单说:
没有缓存区, 那么每 read 一次, 就会发送一次 IO 操作; 有缓存区, 第一次 read 时, 会一下读取 x 个字节放入缓存区,
然后后续的 read 都会从缓存中读取, 当 read 到缓存区末尾时, 会再次读取 x 个字节放入缓存区.
处理流处理数据和节点流处理数据的方法基本上完全相同.
2, 转换流:
转换流作用: 把字节流转换成字符流, 可以解决出现的因为编码集和解码集造成的乱码问题.
- * InputStreamReader:
- * OutputStreamWriter:
* 编码: 字符 -- 编码字符集 ---》二进制
* 解码: 二进制 - 解码字符集 ---》字符
* 在处理文件时, 如果文件的字符格式和编译器处理格式不一样时, 会出现乱码问题. 比如文件字符格式 GBK,
* 而编译器是 UTF-8 格式, 那么就会产生该问题.
*
* 出现乱码问题的原因:
* 1. 编码和解码字符集不一致造成了乱码
* 2. 字节的缺失, 长度的丢失
*
* 大部分情况下, 出现乱码问题是因为中国汉字, 因为中国汉字在不同的字符编码当中占据的字节数不相同, 但是都占据多个字节.
* 而英文字母没有这个问题, 因为英文字母在所有的字符编码当中都占据一个字节.
InputStreamReader : 转换输入流 -》将字节输入流转换成字符输入流
作用: 为了防止文件使用字符输入流处理时出现乱码问题.
节点流:
字节数组流(内存流)
- ByteArrayInputStream
- ByteArrayOutputStream
因为内存输出流当中又新增的方法, 不能使用多态, 不能够让父类的引用指向之类的对象.
作用: 可以在循环当中把所有的数据存放到统一的容器当中, 然后在循环结束之后可以把容器当中所有的内容一起取出来.
注意事项:
内存流属于内存当中的资源, 所以数据量不要过大, 如果太大, 会造成内存溢出的错误.
数据处理流:
- DataOutputStream
- DataInputStream
特点: 既能够保存数据本身, 又能够保存数据类型(基本数据类型 + String)
序列化流:
将对象转换成字节序列的过程, 就是对象序列化过程.
* 将字节序列恢复为对象的过程称之为对象的反序列化.
*
* 作用: 保留对象 (引用数据类型数据的) 类型 + 数据.
*
* 序列化流 : 输出流 ObjectOutputStream writeObject()
* 反序列化流: 输入流 ObjectInputStream readObject()
*
* 注意事项:
* 1. 先序列化然后在反序列化, 而且反序列化的顺序必须和序列化的顺序保持一致.
* 2. 并不是所有的对象都能够被序列化. 只有实现了 Serializable 接口的类的对象才能够被序列化.
* 对象当中并不是所有的属性都能够被序列化.
*
* 对象序列化的主要用途:
* 1. 把对象转换成字节序列, 保存到硬盘当中, 持久化存储, 通常保存为文件.
* 2. 在网络上传递的是对象的字节序列
*
* 对象序列化的步骤:
* 1. 创建对象输出流, 在构造方法当中可以包含其他输出节点流, 如文件输出流.
* 2. 把对象通过 writeObject 的方式写入.
*
* 对象反序列化的步骤:
* 1. 创建对象输入流, 在构造方法当中可以包含其他的输入节点流, 如文件输入流
* 2. 通过 readObject()方法读取对象.
serialVersionUID : 序列化版本 id
* 作用: 从字面角度看, 就是序列化版本号. 凡是实现了 Serializable 接口的类,
* 都会有一个默认的静态的序列化标识.
*
* 1. 类在不同的版本之间, 可以解决序列化兼容问题, 如果之前版本当中在文件中保存对象,
* 那么版本升级后, 如果序列化 id 一致, 我们可以认为文件中的对象依然是此类的对象.
*
* 2. 如果类在不同的版本之间不希望兼容, 但是还希望类的对象能够序列, 那么就在不同版本中
* 使用不同的序列化 id.
*
* transient : 当类中有属性不想被序列化, 那么就使用这个修饰符修饰.
来源: http://www.bubuko.com/infodetail-2825701.html