miracle just wanna be better

序列化

2019-11-01
miracle

认识序列化

序列化有很多机制,这里只介绍java序列化

  • 序列化:java序列化把java对象转换为字节序列的过程
  • 反序列化:把字节序列恢复为java对象的过程

序列化的使用场景

  1. 永久性保存对象,保存队形的字节序列到本地文件或者数据库
  2. 通过序列化以字节流的形式使对象在网络中进行传递和接收

  3. 通过序列化在进程间传递对象

序列化的好处

  1. 实现了数据的持久化
  2. 利用序列化实现远程通信

java序列化机制

  1. 使用Seriallizable接口

先定义一个User类

public class User implements Serializable{
	//序列化Id
	private static final long serialVersionUID=1L;
	private int age;
	private String name;
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public static long getSerialversionuid() {
		return serialVersionUID;
	}
	
}

序列化和反序列化

	//序列化
	private static void SerializeUser() throws Exception{
		//序列化
				User user=new User();
				user.setName("miracle");
				user.setAge(18);
				//序列化到文件中
				ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("D:/test/template"));
				oos.writeObject(user);
				oos.close();
	}
	//反序列化
	private static void DeSerializeUser() throws Exception{
		File file=new File("D:/test/template");
		ObjectInputStream ois=new ObjectInputStream(new FileInputStream(file));
		User newUser=(User)ois.readObject();
	}
}
  1. 使用Externalizable接口

public class User implements Externalizable{
	private int age;
	private String name;
	public User(){}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public void writeExternal(ObjectOutput out) throws IOException {
		out.writeObject(name);
		out.writeInt(age);
	}
	@Override
	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
		name=(String)in.readObject();
		age=in.readInt();	
	}
}

Test和Serializable接口的一样

总结:

  1. Externalizable继承自Serializable接口
  2. 需要重写writeExternal与readExternal方法
  3. 必须要提供一个无参构造

深入分析java序列化机制

  1. serialVersionUID的作用
    • 序列化对象版本控制
    • 如果增加了属性,可以向下兼容,如果删除了一些属性,就不兼容旧数据,InvalidClassException异常

serialVersionUID的生成方式

  • 默认1L
  • 根据类名,接口名,成员方法及属性等生成64位哈希字段
  1. 静态变量的序列化

静态变量不会序列化,因为静态变量在全局区,流里面没有写入静态变量

  1. Transient关键字的作用

Transient关键字的作用是控制变量的序列化,在变量前加上该关键字,可以阻止变量被序列化到文件中,transient变量的值被设为初始值

  1. 深度克隆
  • 浅拷贝:使用一个已知实例对新创建实例的成员变量逐个赋值
  • 深拷贝:当一个类的拷贝构造方法,不仅要复制对象的所有非引用成员变量值,还要为引用类型的成员变量创建创建新的实例,并且初始化为形式参数实例值.

先定义一个工具类

public class CloneUtils {
	public static <T extends Serializable> T clone(T obj){
		T cloneObj=null;
		try {
			//写入字节流
			ByteArrayOutputStream out=new ByteArrayOutputStream();
			ObjectOutputStream obs=new ObjectOutputStream(out);
			obs.writeObject(obj);
			obs.close();
			//分配内存,写入原始对象,生成新对象
			ByteArrayInputStream ios=new ByteArrayInputStream(out.toByteArray());
			ObjectInputStream ois=new ObjectInputStream(ios);
			//返回生成的新对象
			cloneObj =(T)ois.readObject();
			ois.close();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return cloneObj;
	}
}

定义一个Person类

public class Person implements Serializable{
	private static final long serialVersionUID=123456L;
	private String name;
	
	public Person(String name) {
		
		this.name = name;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public static long getSerialversionuid() {
		return serialVersionUID;
	}
	@Override
	public String toString() {
		return "Person [name=" + name + "]";
	}
}

Test类

public class Test {
	public static void main(String[] args) {
		Person person1=new Person("张三");
		Person person2=CloneUtils.clone(person1);
		person2.setName("李四");
		Person person3=CloneUtils.clone(person1);
		person3.setName("王五");
		System.out.println(person1);
		System.out.println(person2);
		System.out.println(person3);
	}
}

结果是
Person [name=张三]
Person [name=李四]
Person [name=王五]

扩展

AVRO,apche提供的一套序列化工具,MapReduce就使用了AVRO


上一篇 代理设计模式

下一篇 冒泡排序

Comments

Content