序列化/反序列化 -写入任意对象与读出的相关问题
序列化
-
java中提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节对象那个包括对象的数据、对象的类型、对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息
-
反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据、对象的类型和对象中存储的数据信息,都可以用来在内存中创建对象。
对象的序列化流:ObjectOutputStream
-
java.lang.Object
继承者 java.io.OutputStream
继承者 java.io.ObjectOutputStream -
作用:把对象以流的方式写入文件保存
-
构造方法:
- void ObjectOutputStream(OutputStream out)
创建写入指定 OutputStream 的 ObjectOutputStream。
- void ObjectOutputStream(OutputStream out)
-
使用步骤:(写入的对象的类必须实现Serializable接口)
package objectStream;
import java.io.*;
public class ObjectStream {
public static void main(String[] args) throws IOException {
//1.创建序列化流,构造方法中创建字节输出流绑定写入位置
ObjectOutputStream objstr = new ObjectOutputStream(new FileOutputStream("b.txt"));
//2.使用ObjectOutputStream的writeObject方法写入对象,写入文件中
objstr.writeObject(new Person("小王",22));
//3.释放资源
objstr.close();
/*
java.io.NotSerializableException: objectStream.Person
抛出异常,对象未实现标记型接口Serializable
有标记,则允许序列化和反序列化,若无,则不能
*/
}
}
对象的反序列化流:ObjectInputStream
- java.lang.Object
继承者 java.io.InputStream
继承者 java.io.ObjectInputStream -
作用:把文件中的对象,以流的方式读取出来使用
-
构造方法:
ObjectInputStream(InputStream in)
创建从指定 InputStream 读取的 ObjectInputStream。 -
使用步骤
package objectStream;
import java.io.*;
public class ObjectStream {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//1.创建反序列化流,构造方法中创建字节输入流绑定输入位置
ObjectInputStream objIn= new ObjectInputStream(new FileInputStream("b.txt"));
//2.使用readObject读取文件中的对象,并返回对象
Object obj = objIn.readObject();
//3.打印看看
System.out.println(obj);
/*
需要抛出一个:ClassNotFoundException异常
*/
}
}
反序列化操作问题
-
当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作会失败,抛出“InvalidClassException”异常,该异常有以下原因:
- 该类的序列版本号从流中读取的类描述符的版本号不匹配
- 该类包含未知数据类型
- 该类没有可访问的无参数构造方法
-
Serializble接口给需要序列化的类,提供了一个序列版本号。serialVersionUID该版本号的目的在于验证序列化的对象和对应类是否版本匹配。序列号编译时生成在字节码文件中。
解决方案:手动添加一个序列版本号
格式:private static final long serialVersionUID = xxxL;
package objectStream;
import java.io.Serializable;
public class Person implements Serializable {
private static final long serialVersionUID = 23L;
private String name;
private int 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 +
'}';
}
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;
}
}
transient关键字:瞬态关键字
-
静态优先于非静态加载到内存中(静态优先于对象)被static修饰的类的成员变量是不可被序列化,序列化修饰的是对象。即在写入对象时,被static修饰的成员变量不会改变。
-
同样,被transient修饰的成员变量,不可被序列化
序列化集合
- 当我们想在文件中保存多个对象的时候,可以把多个对象存储到一个集合中,对集合进行序列化和反序列化
练习
package objectStream;
import java.io.*;
import java.util.ArrayList;
import java.util.Scanner;
public class practice {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//1.定义一个集合,类型为Person
ArrayList<Person> serialList = new ArrayList<>();
//2.向集合中添加存储对象
serialList.add(new Person(new Scanner(System.in).nextLine(),new Scanner(System.in).nextInt()));
serialList.add(new Person(new Scanner(System.in).nextLine(),new Scanner(System.in).nextInt()));
//3.使用ObjectOutputStream进行序列化,输出到文件
ObjectOutputStream objout=new ObjectOutputStream(new FileOutputStream("b.txt"));
//4.使用writeObject方法,将集合对象写入文件
objout.writeObject(serialList);
//5.把集合从文件读取回来
ObjectInputStream objin =new ObjectInputStream(new FileInputStream("b.txt"));
//6.转变类型
Object readList= objin.readObject();
ArrayList<Person> rL = (ArrayList<Person>)readList;
//7.看一哈
System.out.println(rL);
//释放资源
objout.close();
objin.close();
}
}