对象流ObjectInputStream和ObjectOutputStream介绍
一、对象流
用于存储和读取基本数据类型数据或对象的处理流。
它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。
二、序列化和反序列化
ObjectOutputStream 类 : 把内存中的Java对象转换成平台无关的二进制数据,从而允许把这种二进制数据持久地保存在磁盘上,或通过网络将这种二进制数据传输到另一个网络节点。 -- 序列化
用ObjectInputStream类 : 当其它程序获取了这种二进制数据,就可以恢复成原来的Java对象。 -- 反序列化
三、代码(操作字符串对象)
首先将一个字符串对象写到文件中去:序列化
public class Test01 {
//这是一个main方法,是程序的入口:
public static void main(String[] args) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("d:\\Demo3.txt")));
//将内存中的字符串写出到文件中:
oos.writeObject("你好");
//关闭流:
oos.close();
}
}
查看文件:
我们看不懂文件的内容,但是程序是可以看懂的,所以可以写一个程序读文件中内容(反序列化)
public class Test02 {
//这是一个main方法,是程序的入口:
public static void main(String[] args) throws IOException, ClassNotFoundException {
//将文件中保存的字符串 读入到 内存:
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("d:\\Demo3.txt")));
//读取:
String s = (String)(ois.readObject());
System.out.println(s);
//关闭流:
ois.close();
}
}
控制台:
四、代码(操作自定义类的对象)
自定义的Person类
public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
测试类:
public class Test01 {
//这是一个main方法,是程序的入口:
public static void main(String[] args) throws IOException {
//序列化:将内存中对象 ---》 文件:
//有一个对象:
Person p = new Person("lili",19);
//有对象流:
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("d:\\Demo4.txt")));
//向外写:
oos.writeObject(p);
//关闭流:
oos.close();
}
}
运行的时候发现出现异常:
出现异常的原因:
你想要序列化的那个对象对应的类,必须要实现一个接口:
接口内部,什么都没有,这种接口叫 标识接口。
起到标识作用,标识什么呢?只要实现这个接口的类的对象才能序列化,否则不可以。
解决办法:
将Person 实现这个标识接口就可以
public class Person implements Serializable {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
测试:
发现序列化成功,Person具备了序列化的能力。
这个二进制数据我们看不懂,但是程序可以看懂,所以我们可以用程序实现反序列化操作:
将这个对象恢复到内存中来:
public class Test02 {
//这是一个main方法,是程序的入口:
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("d:\\Demo4.txt")));
//读入内存:
Person p = (Person)(ois.readObject());
System.out.println(p/*.toString()*/);
//关闭流:
ois.close();
}
}
结果:
因为我们没有重写toString方法,所以结果为
证明了反序列化成功,将二进制数据 -- 内存
五、serialVersionUID
凡是实现Serializable接口(标识接口)的类都有一个表示序列化版本标识符的静态常量:
private static final long serialVersionUID;
serialVersionUID用来表明类的不同版本间的兼容性。简言之,其目的是以序列化对象进行版本控制,有关各版本反序加化时是否兼容。
如果类没有显示定义这个静态变量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID 可能发生变化。故建议,显式声明。
简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException)
现在在Person类中加入toString方法:
public class Person implements Serializable {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
再次运行测试类,出现异常:
出现异常的原因:
解决:
给这个类 加入一个 序列号:serialVersionUID
六、IDEA中配置序列化版本号
在Person类上:alt+enter
回车即可生成
七、序列化细节
1、被序列化的类的内部的所有属性,必须是可序列化的 (基本数据类型都是可序列化的)
2、static,transient修饰的属性 不可以被序列化
public class Person implements Serializable {
private static final long serialVersionUID = 8027651838638826533L;
private transient String name;
private static int age;
private Famaily f = new Famaily();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person() {
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", f=" + f + ",age=" + age +
'}';
}
}
结果: