天天看点

Java序列化基础

简介

序列化:将对象写入到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