对象流:ObjectInputStream , ObjectOutputStream
【1】对象流:ObjectInputStream,ObjectInputStream
用于存储和读取基本数据类型数据或对象的处理流。
它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。
其实就是:
【2】序列化和反序列化
ObjectOutputStream 类 : 把内存中的Java对象转换成平台无关的二进制数据,从而允许把这种二进制数据持久地保存在磁盘上,或通过网络将这种二进制数据传输到另一个网络节点。---->序列化
用ObjectInputStream类 : 当其它程序获取了这种二进制数据,就可以恢复成原来的Java对象。----> 反序列化
【3】代码:操作字符串对象
首先将一个字符串对象写到文件中去:----> 序列化
package test7_Object_stream;
import java.io.*;
/**
* @Auther: zhoulz
* @Description: test7_Object_stream
* @version: 1.0
*/
public class Test1 {
public static void main(String[] args) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("d:\\Demo3.txt")));
//将内存中的字符串写出到文件中:
oos.writeObject("你好周");
//关闭流
oos.close();
}
}
查看文件:
我们看不懂文件的内容,但是程序是可以看懂的。
所以可以写一个程序读文件中内容:—> 反序列化
代码示例:
package test7_Object_stream;
import java.io.*;
/**
* @Auther: zhoulz
* @Description: test7_Object_stream
* @version: 1.0
*/
public class Test2 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//将文件中保存的字符串 读入到 内存:
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("d:\\Demo3.txt")));
//读取:
//Object o = ois.readObject();
//因为提前知道是字符串,所以可整体转为字符串类型
String s = (String)(ois.readObject());
System.out.println(s);
//关闭流
ois.close();
}
}
结果:
上面是:操作字符串对象
接下来:
【4】代码:操作自定义类的对象
自定义的Person类:
package test7_Object_stream;
/**
* @Auther: zhoulz
* @Description: test7_Object_stream
* @version: 1.0
*/
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;
}
}
测试类:
package test7_Object_stream;
import java.io.*;
/**
* @Auther: zhoulz
* @Description: test7_Object_stream —— 操作自定义类的对象
* @version: 1.0
*/
public class Test3 {
public static void main(String[] args) throws IOException {
//序列化:将内存中对象 ---> 文件
//有一个对象
Person p = new Person("lili",20);
//有对象流:
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("d:\\Demo4.txt")));
//向外写:
oos.writeObject(p);
//关闭流
oos.close();
}
}
运行的时候发现出现异常:
出现异常的原因:
你想要序列化的那个对象对应的类,必须要实现一个接口:
接口内部,什么都没有,这种接口叫 标识接口。
起到标识作用,标识什么呢?
:只要实现这个接口的类的对象才能序列化,否则不可以。
解决办法:将Person 实现这个标识接口就可以。
即:
测试:发现序列化成功,Person具备了序列化的能力。
这个二进制数据我们看不懂,但是程序可以看懂,所以我们可以用程序实现 反序列化操作:
将这个对象 恢复到内存中来:
代码示例:
package test7_Object_stream;
import java.io.*;
/**
* @Auther: zhoulz
* @Description: test7_Object_stream —— 反序列化
* @version: 1.0
*/
public class Test3_2 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("d:\\Demo4.txt")));
//读入内存:
//Object o = ois.readObject();
//知道是Person类型,所以直接转换:
Person p = (Person)(ois.readObject());
System.out.println(p.toString());
//结果;test7_Object_stream.Person@4dd8dc3 —— 因为没有重写toString()方法
//关闭流:
ois.close();
}
}
结果:(因为我们没有重写toString方法)
证明了反序列化成功: 将二进制数据 ——> 内存
【5】serialVersionUID
凡是实现Serializable接口(标识接口)的类都有一个表示序列化版本标识符的静态常量:
➢private static final long serialVersionUID;
➢serialVersionUID用来表明类的不同版本间的兼容性。简言之,其目的是以序列化对象进行版本控制,有关各版本反序加化时是否兼容。
➢如果类没有显示定义这个静态变量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID 可能发生变化。故建议,显式声明。
➢简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException)
我现在在Person类中加入toString方法:
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
再次运行测试类:
出现异:
出现异常的原因:
解决:给这个类 加入一个 序列号:serialVersionUID
结果:
另注:
针对以上加入toString()方法后,出现运行报错的问题:
先重新进行序列化,然后再进行反序列化 —— 则可以正常运行。 ???
【6】IDEA中配置序列化版本号
在Person类上:alt+enter:
回车即可生成:
【7】序列化细节
(1)被序列化的类的内部的所有属性,必须是可序列化的;
(基本数据类型都是可序列化的)
(2)static,transient修饰的属性 不可以被序列化。
结果:
想要保护的属性可以用 transient修饰 ;
静态的,被这些类共享的属性,则用static 修饰。