缓冲流
缓冲流:文件输入流与文件输出流的加强板,它的的读写速度比文件输入流或文件输出流更快。
-
字节缓冲流:
BufferedInputStream
,BufferedOutputStream
-
字符缓冲流:
BufferedReader
,BufferedWriter
创建缓冲流的对象时一般调用当前类的带一个参数(对应的字节(或字符)输入(或输出)流对象)的构造器,之后的写入或读出数据的步骤与对应的输入输出流的写法大致相同,由于字符缓冲流运用public void newLine()
:方法读写文本文件时存在缺陷(即:当第一行文本还没有读写完毕就做了换行操作,从而是读取的第一行文本的数据不完整),所以还是用原本的字符输出流中的read方法读写文本文件。
转换流
编码与解码:
按照某种规则,将字符存储到计算机中,称为编码 。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码 。
编码:字符(能看懂的)--字节(看不懂的)
解码:字节(看不懂的)-->字符(能看懂的)
字符编码Character Encoding
: 就是一套自然语言的字符与二进制数之间的对应规则。
我们通常使用的字符集 Charset
:也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。
计算机要准确的存储和识别各种字符集符号,需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBK字符集、Unicode字符集等。可见,当指定了编码,它所对应的字符集自然就指定了,所以编码才是我们最终要关心的。
当一个文件到的编码格式与操作系统的默认编码格式不一致时,读取该文件的时候会出现乱码的情况,写入数据是会卡bug,所以就要用转换流来解决这些问题。
转换流java.io.InputStreamReader
,是Reader的子类,是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。
构造方法
-
InputStreamReader(InputStream in)
: 创建一个使用默认字符集的字符流。 -
InputStreamReader(InputStream in, String charsetName)
: 创建一个指定字符集的字符流。
构造举例,代码如下:
InputStreamReader isr = new InputStreamReader(new FileInputStream("in.txt"));
InputStreamReader isr2 = new InputStreamReader(new FileInputStream("in.txt") , "GBK");
转换流java.io.OutputStreamWriter
,是Writer的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。
构造方法
-
OutputStreamWriter(OutputStream in)
: 创建一个使用默认字符集的字符流。 -
OutputStreamWriter(OutputStream in, String charsetName)
: 创建一个指定字符集的字符流。
构造举例,代码如下:
OutputStreamWriter isr = new OutputStreamWriter(new FileOutputStream("out.txt"));
OutputStreamWriter isr2 = new OutputStreamWriter(new FileOutputStream("out.txt") , "GBK");
序列化与反序列化
一、序列化。运用java.io.ObjectOutputStream
类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。
序列化操作
-
一个对象要想序列化,必须满足两个条件:
-
该类必须实现
java.io.Serializable
接口,Serializable
是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException
。 -
该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用
transient
关键字修饰。
2.写出对象方法
-
public final void writeObject (Object obj)
: 将指定的对象写出。
注意:进行序列化操作之前必须先用public ObjectOutputStream(OutputStream out)
: 创建一个指定OutputStream的ObjectOutputStream。构造举例,代码如下:
FileOutputStream fileOut = new FileOutputStream("employee.txt");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
二、反促裂化。运用ObjectInputStream反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象。
步骤:首先运用public ObjectInputStream(InputStream in)
: 创建一个指定InputStream的ObjectInputStream。再进行反序列化操作
如果能找到一个对象的class文件,我们可以进行反序列化操作,调用ObjectInputStream
读取对象的方法:
用public final Object readObject ()
: 读取一个对象。代码如下:
public class DeserializeDemo {
public static void main(String [] args) {
Employee e = null;
try {
// 创建反序列化流
FileInputStream fileIn = new FileInputStream("employee.txt");
ObjectInputStream in = new ObjectInputStream(fileIn);
// 读取一个对象
e = (Employee) in.readObject();
// 释放资源
in.close();
fileIn.close();
}catch(IOException i) {
// 捕获其他异常
i.printStackTrace();
return;
}catch(ClassNotFoundException c) {
// 捕获类找不到异常
System.out.println("Employee class not found");
c.printStackTrace();
return;
}
// 无异常,直接打印输出
System.out.println("Name: " + e.name); // zhangsan
System.out.println("Address: " + e.address); // beiqinglu
System.out.println("age: " + e.age); // 0
}
}
当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException
异常。发生这个异常的原因如下:
-
该类的序列版本号与从流中读取的类描述符的版本号不匹配
-
该类包含未知数据类型
-
该类没有可访问的无参数构造方法
Serializable
接口给需要序列化的类,提供了一个序列版本号。serialVersionUID
该版本号的目的在于验证序列化的对象和对应类是否版本匹配。
序列化集合
-
将存有多个自定义对象的集合序列化操作,保存到
list.txt
文件中。 -
反序列化
list.txt
,并遍历集合,打印对象信息。
步骤:
-
把若干对象 ,保存到集合中。
-
把集合序列化。
-
反序列化读取时,只需要读取一次,转换为集合类型。
-
遍历集合,可以打印所有的对象信息