注:
①序列化是一种对象持久化的手段,普遍应用在网络传输、RMI等场景;
②对象流:对象作为一种复合型数据,不仅包括多种不同类型的属性数据,还有和类相关的信息,所以简单的流处理无法实现对象的传输和永久保存。为此,Java提供了对象流和对象序列化机制,来保证对象作为一个整体进行I/O流传输。
对象的序列化是将对象转换成字节序列,把字节序列恢复为对象的过程称为对象的反序列化。
一个类如果实现了java.io.Serializable接口,这个类的实例(对象)就是一个序列化的对象。Serializable接口中没有方法,因此实现该接口的类不需要额外实现其他的方法。可序列化类的所有子类型本身都是可序列化的。
当实现了该接口的对象进行输出时,JVM将按照一定的格式(序列化信息)转换成字节进行传输和存储到目的地。当对象输入流从文件或网络上读取对象时,会先读取对象的序列化信息,并根据这一信息创建对象。
实现了序列化接口的类中会有一个Long型的静态常量serialVersionUID,这个值用于识别具体的类。如果不设置,JVM会自动分配一个值。为了保证类的准确识别,在定义类时应显示设置该值。
字面意思上是序列化的版本号,凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量。
①把对象保存到文件中(存储到物理介质)
②对象需要在网络上传输时
构造方法: public ObjectInputStream(InputStream in) //创建从指定输入流读取的ObjectInputStream
Object readObject() // 从ObjectInputStream流中读取对象
构造方法:public ObjectOutputStream(OutputStream out) //创建写入指定输出流的ObjectInputStream对象
void writeObject(Object o) // 将指定对象o写入ObjectOutputStream流中
import java.io.*;
import java.util.Scanner;
public class ObjectStream {
public static void main(String[] args) {
try {
File file;// File类
FileInputStream fin;// 文件字节输入流
FileOutputStream fout;// 文件字节输出流
ObjectInputStream oin;// 对象输入流
ObjectOutput oout;// 对象输出流
Scanner scanner = new Scanner(System.in);
System.out.println("请输入文件名,例如d:\\foo");
String filename = scanner.nextLine();
file = new File(filename);// 创建文件对象
if (!file.exists()) {// 如果文件不存在
file.createNewFile();// 创建新文件
}
// 序列化
fout = new FileOutputStream(file);// 实例化文件字节输出流
oout = new ObjectOutputStream(fout);// 实例化对象输出流
Person person = new Person("张三", 20);// person此时是一个序列化的对象
oout.writeObject(person);// 用writeObject将对象写入到文件中,序列化的标志
System.out.println("序列化成功!");
oout.close();// 关闭对象输出流
fout.close();// 关闭文件输出流
System.out.println("对象写入完毕!");
// 反序列化
System.out.println("文件" + filename + "的内容是:");
Person object;
fin = new FileInputStream(file);// 创建文件输入流
oin = new ObjectInputStream(fin);// 创建对象输入流
try {
object = (Person) oin.readObject();// 强制转换,读取对象中的信息,反序列化的标志
System.out.println("反序列化成功!");
System.out.println("读取的对象信息:");
System.out.println("用户名:" + object.getName());
System.out.println("年龄:" + object.getAge());
} catch (ClassNotFoundException e) {
System.out.println("读取对象失败!");
}
oin.close();// 关闭对象输入流
fin.close();// 关闭文件输入流
} catch (IOException e) {
e.printStackTrace();
}
}
}
class Person implements Serializable {// 对象序列化
private static final long serialVersionUID = 1234567890L;// 设置Long型的静态常量serialVersionUID,用于识别具体的类
String name;
int age;
public Person(String name, int age) {// 含参构造
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName() {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge() {
this.age = age;
}
}
有时候需要将一个类的很多对象的信息写入文件,方便二次读取,但是如果一个对象所包含的属性太多,将这些属性信息依次写入文件所需要的代码比较繁杂,为了省事可以使用writeObject方法直接写入对象。读取信息时,只需要使用readObject方法直接读出来。 需要注意的是,使用writeObject所写入的类必须要实现Serializable接口!
public final void writeObject(Object obj) throws IOException // 将指定的对象写入ObjectOutputStream
obj -----> 要写入的对象
a、当对象被序列化时,JVM只序列化其字段值,方法和构造器不参与序列化;
b、如果对象的某个字段引用了另一个对象,则被引用对象的字段也将被序列化;
c、有些对象类(如FileInputStream)是不可序列化的,因为字段值与操作系统相关的即时信息;
d、访问控制修饰符对序列化无影响;
e、静态变量不参与序列化,因为它不属于对象。
对象的序列化:将对象的属性值以字节的形式存储到文件中,是个将对象的状态信息转换为可存储或传输的形式的过程。
对象的反序列化:读取文件的字节内容(原对象的属性值)生成新的对象就叫反序列化。
Dog dog = new Dog("haha",3,"雄",0);
Dog dog2 = new Dog("lala",2,"雄",1);
Dog dog3 = new Dog("wawa",4,"雌",2);
Dog[] dogs = {dog,dog2,dog3};//将对象存在数组中
-----> oos.writeObject(dogs); //对一组进行序列化
//当反序列化时,对象数组生成新的对象数组
Dog[] dog = (Dog[])ois.readObject(); //读取的是对象数组的序列化,所有需要强转型为Dog
类型的数组,接收也是Dog类型的数组
a、在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是Web服务器中的Session对象,当有10万用户并发访问,就可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些Session先序列化到硬盘中,等要用的时候再把保存在硬盘中的对象还原到内存中。
b、当两个进程在进行远程通信时,彼此可以发送各种类型数据,无论是何种类型的数据,都会以二进制序列的形式在网络上传送,发送方需要把这个Java对象转换为字符序列才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。
1)创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流(FileOutputStream);
2)通过对象输出流的writeObject()方法写对象。
1)创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流(FileInputStream);
2)通过对象输入流的readObject()方法读取对象。
a、serialVersionUID的取值是Java运行时环境根据类的内部细节自动生成的。如果对类的源代码做了修改,再重新编译的话,新生成的类文件的serialVersionUID的取值有可能发生变化;
b、类的serialVersionUID完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,有可能会导致不同的serialVersionUID,也有可能相同;
c、为了提高serialVersionUID的独立性和确定性,应在一个可序列化的类中显示定义serialVersionUID,赋予明确的值;
d、阿里巴巴要求程序员谨慎修改serialVersionUID 字段的值。
serialVersionUID是用来验证版本一致性的。所以在做兼容性升级的时候,不要改变类中serialVersionUID的值。
serialVersionUID 既然是验证版本一致性的,在做版本升级的时候(非兼容性升级),记得要修改这个字段的值,这样可以避免序列化混乱。
a、在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;
b、在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。