简介
序列化:将对象写入到IO流中
反序列化:从IO流中恢复对象
意义:序列化机制允许将实现序列化的Java对象转换位字节序列,这些字节序列可以保存在磁盘上,或通过网络传输,以达到以后恢复成原来的对象。序列化机制使得对象可以脱离程序的运行而独立存在。
使用场景:所有可在网络上传输的对象都必须是可序列化的,比如RMI(remote method invoke,即远程方法调用),传入的参数或返回的对象都是可序列化的,否则会出错;所有需要保存到磁盘的java对象都必须是可序列化的。通常建议:程序创建的每个JavaBean类都实现Serializeable接口。
注意事项
1.实现方式有实现Serializable接口和实现Externalizable接口两种模式
2.可序列化的类加上serialVersionUID 版本号,有利于JVM迁移
3.对象类名,实例变量包括基本类型,数组,引用都会被序列化(transient实例变量不被序列化);方法和类变量都不会被序列化
4.反序列化必须按照写入顺序
5.单例类序列化需要重写readResolve方法
6.对象不会重复序列化,多次序列化时只有第一次序列化为字节流,其他保存序列号
7.反序列化创建对象不会调用构造方法
序列化实现的方式
如果需要将某个对象保存到磁盘上或者通过网络传输,那么这个类应该实现Serializable接口或者Externalizable接口之一
1.java默认为Serializable接口提供序列化和反序列化支持,一旦实现这个接口就表明该对象支持序列化。
/**
*
*/
package com.mlycan.smoon.home.serializable;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* @author Administrator
*
*/
public class Person implements Serializable{
/**
*
*/
private static final long serialVersionUID = 6950744995811540444L;
/**
*
*/
public Person() {
}
private String name;
private int age;
//我不提供无参构造器
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;
}
public static void main(String[] args) {
//1.try-with-resource创建文件流
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:/eclipse/workspace/work/person.txt"))){
Person person = new Person("yuwei", 23);
//2.将对象序列化到文件
oos.writeObject(person);
} catch (Exception e) {
e.printStackTrace();
}
//3.反序列化
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:/eclipse/workspace/work/person.txt"))) {
Person brady = (Person) ois.readObject();
System.out.println(brady);
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.序列化对象的引用也必须是序列化,当引用的非序列化对象不赋值时可以正常序列化,当引用对象赋值时序列化会抛出java.io.NotSerializableException异常
/**
*
*/
package com.mlycan.smoon.home.serializable;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* @author Administrator
*
*/
class Student {
public Student() {
// TODO Auto-generated constructor stub
}
private String name;
private int age;
//我不提供无参构造器
public Student(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;
}
}
public class Person implements Serializable{
/**
*
*/
private static final long serialVersionUID = 6950744995811540444L;
/**
*
*/
public Person() {
}
private String name;
private int age;
private Student student;
//我不提供无参构造器
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;
}
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
public static void main(String[] args) {
//1.try-with-resource创建文件流
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:/eclipse/workspace/work/person.txt"))){
Person person = new Person("yuwei", 23);
Student student = new Student("yuwei", 23);
person.setStudent(student);
//2.将对象序列化到文件
oos.writeObject(person);
} catch (Exception e) {
e.printStackTrace();
}
//3.反序列化
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:/eclipse/workspace/work/person.txt"))) {
Person brady = (Person) ois.readObject();
System.out.println(brady);
} catch (Exception e) {
e.printStackTrace();
}
}
}
异常:
java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: com.mlycan.smoon.home.serializable.Student
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1571)
at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2278)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2202)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2060)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1567)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:427)
at com.mlycan.smoon.home.serializable.Person.main(Person.java:110)
3.自定义序列化
自定义序列化可以通过transient关键字设置需要实例化的属性,也可以通过实现writeReplace和readResolve()方法实现,或者通过实现Externalizable接口强制自定义序列化。这里主要介绍一下通过实现Externalizable接口,必须实现writeExternal、readExternal方法。
/**
*
*/
package com.mlycan.smoon.home.serializable;
import java.io.Externalizable;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
/**
* @author Administrator
*
*/
public class ExPerson implements Externalizable {
/**
*
*/
public ExPerson() {
// TODO Auto-generated constructor stub
}
private String name;
private int age;
//我不提供无参构造器
public ExPerson(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;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
//将name反转后写入二进制流
StringBuffer reverse = new StringBuffer(name).reverse();
System.out.println(reverse.toString());
out.writeObject(reverse);
out.writeInt(age);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.name = ((StringBuffer) in.readObject()).reverse().toString()+"-external";;
System.out.println(name);
this.age = in.readInt()+1;
}
/**
* @param args
*/
public static void main(String[] args) throws IOException, ClassNotFoundException {
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ExPerson.txt"));
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ExPerson.txt"))) {
oos.writeObject(new ExPerson("brady", 23));
ExPerson ep = (ExPerson) ois.readObject();
System.out.println(ep);
}
}
}
输出结果:
ydarb
brady-external
Person{name='brady-external', age=24}
在重写readExternal时,改变了序列化对象的属性值,所以在这里看到和原来序列化时不一样的结果。
参考:
本文只写了我个人关注的部分,更具体的内容建议参考下面这篇博客,非常完整。
https://www.cnblogs.com/9dragon/p/10901448.html